- 題名: 異なるプロジェクト間で基底・派生クラス作成時、基底クラスが参照できない
- 日時: 2009/12/04 12:10:17
- ID: 25962
- この記事の返信元:
- (なし)
- この記事への返信:
- [25964] Re[1]: 異なるプロジェクト間で基底・派生クラス作成時、基底クラスが参照できない2009/12/04 12:31:15
- [25976] Re[1]: 異なるプロジェクト間で基底・派生クラス作成時、基底クラスが参照できない2009/12/04 15:59:28
- ツリーを表示
■No25973に返信(わんこさんの記事)
■No25967に返信(わんこさんの記事)
> ただ、ということは、この手のクラスライブラリを使用する際、ユーザ(使い手側)は
> 常に使用したいクラスの基底クラスを知っておいて、自作プロジェクトに参照追加
> しなくてはならないということになるのでしょうか....
>
> 使いづらくなってしまいますね
普通、ツリー状に実装するので大して使いにくくはならないかと思います。
変な事すると循環参照の罠にかかるので注意する必要がありますしね。
管理するだけ(コード実装しない)のであれば、クラスではなくインターフェイスのが良いです。
---------------------------
◇ 基本ライブラリ
名前空間 : ObjectLibrary.ObjectModel
アセンブリ名 : base.dll
Public Interface IBase
Function GetStringBase() As String
End Interface
---------------------------
◇ サブライブラリ1
名前空間 : ObjectLibrary
アセンブリ名 : sub1.dll
参照 : base.dll
Imports ObjectLibrary.ObjectModel
Public Class Sub1
Implements IBase
Public Overridable Function GetStringBase() As String Implements IBase.GetStringBase
Return "Sub1"
End Function
End Class
---------------------------
◇ サブライブラリ2
名前空間 : ObjectLibrary
アセンブリ名 : sub2.dll
参照 : base.dll
Imports ObjectLibrary.ObjectModel
Public Class Sub2
Implements IBase
Public Function GetStringBase() As String Implements IBase.GetStringBase
Return "Sub2"
End Function
End Class
---------------------------
◇ 最終利用 (コンソールプロジェクト)
参照 : base.dll, sub1.dll, sub2.dll
Imports ObjectLibrary.ObjectModel
Imports ObjectLibrary
Sub Main()
' インターフェイスで変数を作成
Dim base As IBase = Nothing
' サブクラス1をキャスト
base = New Sub1()
Console.WriteLine(base.GetStringBase())
' サブクラス2をキャスト
base = New Sub2()
Console.WriteLine(base.GetStringBase())
End Sub
---------------------------
ちなみに…
インターフェイスは複数実装できるので、複数のインターフェイスを掛け合わせて作ることもできます。
---------------------------
◇ 基本ライブラリ ( に追加 )
名前空間 : ObjectLibrary.ObjectModel
アセンブリ名 : base.dll
Public Interface IBase2
Function GetStringOverSub() As String
End Interface
---------------------------
◇ サブライブラリ3
名前空間 : ObjectLibrary
アセンブリ名 : sub3.dll
参照 : base.dll, sub1.dll
Imports ObjectLibrary.ObjectModel
' 両方のインターフェイスを実装
Public Class Sub3
Implements IBase
Implements IBase2
Public Function GetStringBase() As String Implements IBase.GetStringBase
Return "Sub3"
End Function
Public Function GetStringOverSub() As String Implements IBase2.GetStringOverSub
Return "OverSub3"
End Function
End Class
' サブクラス1にインターフェイスを追加実装
Public Class Sub4
Inherits Sub1
Implements IBase2
Public Overrides Function GetStringBase() As String
Return "Sub4"
End Function
Public Function GetStringOverSub() As String Implements IBase2.GetStringOverSub
Return "OverSub4"
End Function
End Class
---------------------------
◇ 最終利用 (コンソールプロジェクト)
参照 : base.dll, sub1.dll, sub2.dll, sub3.dll
Imports ObjectLibrary.ObjectModel
Imports ObjectLibrary
Sub Main()
' 各サブクラスを WriteLineメソッドで表示する
WriteLine(New Sub1())
WriteLine(New Sub2())
WriteLine(New Sub3())
WriteLine(New Sub4())
End Sub
' 表示用メソッド
Sub WriteLine(ByVal base As IBase)
' IBase の GetStringBase メソッドを表示
Console.WriteLine(base.GetStringBase())
' IBase2が実装されているか確認。されてなければ終了。
If (TypeOf base Is IBase2) = False Then Return
' IBase2 にキャストして GetStringOverSub メソッドを呼ぶ
Console.WriteLine(DirectCast(base, IBase2).GetStringOverSub())
End Sub
---------------------------
こんな事もできますよ。って例でした。
> .NET Framework自体もこのような構成になっているのではないかと思うんですが
> どのように実装しているのかわかればな
ガイドラインと構成は、MSDNに書いてあるので探してみるといいかも。
単純にコードを見たいのであれば、こちらへ。(英語)
http://referencesource.microsoft.com/
追加です(笑
ほかのアセンブリのクラスを使う場合にはラッパークラスを作るのが有効です
---------------------------
◇ サブライブラリ4
名前空間 : ObjectLibrary
アセンブリ名 : sub4.dll
参照 : base.dll
Imports ObjectLibrary.ObjectModel
Public Class Sub5
Implements IBase
Implements IBase2
' 普通のコンストラクタ
Public Sub New()
Me.New(Nothing)
End Sub
' ラッピング元のインスタンスを指定したコンストラクタ
Public Sub New(ByVal baseInstance As IBase)
Me.baseInstance = baseInstance
End Sub
' ラッピング元のインスタンス
Private baseInstance As IBase
Public Function GetStringBase() As String Implements IBase.GetStringBase
If (Me.baseInstance Is Nothing) Then Return "Sub5"
Return "Sub5 with " + Me.baseInstance.GetStringBase()
End Function
Public Function GetStringOverSub() As String Implements IBase2.GetStringOverSub
If (Me.baseInstance Is Nothing Or (Not Me.baseInstance Is Nothing AndAlso (TypeOf Me.baseInstance Is IBase2) = False)) Then Return "OverSub5"
Return "OverSub5 with " + DirectCast(Me.baseInstance, IBase2).GetStringOverSub()
End Function
End Class
---------------------------
◇ 最終利用 (コンソールプロジェクト)
参照 : base.dll, sub3.dll, sub4.dll
Imports ObjectLibrary.ObjectModel
Imports ObjectLibrary
Sub Main()
' 各サブクラスを WriteLineメソッドで表示する
WriteLine(New Sub5())
WriteLine(New Sub5(New Sub3()))
WriteLine(New Sub5(New Sub4()))
End Sub
' 表示用メソッド
Sub WriteLine(ByVal base As IBase)
' IBase の GetStringBase メソッドを表示
Console.WriteLine(base.GetStringBase())
' IBase2が実装されているか確認。されてなければ終了。
If (TypeOf base Is IBase2) = False Then Return
' IBase2 にキャストして GetStringOverSub メソッドを呼ぶ
Console.WriteLine(DirectCast(base, IBase2).GetStringOverSub())
End Sub
---------------------------
2009/12/04(Fri) 16:41:56 編集(投稿者) 2009/12/04(Fri) 16:40:03 編集(投稿者) > ただ、ということは、この手のクラスライブラリを使用する際、ユーザ(使い手側)は > 常に使用したいクラスの基底クラスを知っておいて、自作プロジェクトに参照追加 > しなくてはならないということになるのでしょうか.... > > 使いづらくなってしまいますね それは仕方ないと思います。 ただし、「名前空間」−「アセンブリ」−「クラス」の関係をユーザーが意識できるよう 関連付けているなら、ユーザーもさほど混乱なくクラスを使用できると思います。 たとえば1例として現在携わっているシステムの場合。 ソリューションに以下のようにプロジェクトを設けてます。 ■まず「プロジェクト名=ネームスペース名」になるよう慎重に定義します。 xxxx.Tools.Controls 汎用カスタムコントロールを提供します。 xxxx.Tools.Database データベースとアクセス用の汎用クラスを提供します。 xxxx.Tools.Utility アプリケーション全体で使用される汎用的な機能を提供します。 xxxx.Business.Master マスタ参照・更新用の機能を提供します。 xxxx.Business.Map 地図コンポーネントを提供します。 xxxx.Business.Sales 販売業務の参照・更新用の機能を提供します。 ■そして xxxx.Tools.Controls には自作のカスタムコントロールを加えます。 // 拡張テキストボックス xxxx.Tools.Controls.ExTextBox // 拡張データグリッドビュー xxxx.Tools.Controls.ExDataGridView ■また xxxx.Tools.Database には ADO.NET を隠蔽したクラスを設けます。 // ADO.NET をカプセル化し、簡素化したインターフェイスを提供します。 xxxx.Tools.Database.DataFacade ■業務で使う地図機能は、xxxx.Business.Map 名前空間にまとめます。 // 配送ルートを表示する地図コントロールです。 xxxx.Business.Map.MapControl ■販売集計用の画面は当然以下のようになります。 // 販売集計用画面です。 xxxx.Business.Sales.SalesSummaryForm こんな具合に「名前空間」に対して「クラス」が明確な意図を持った配置がなされていれば ユーザーはどこに何があるのか、このクラスはどのアセンブリにあるのかが推測しやすくなります。 もちろん最初は存在が曖昧なクラスもありますし、どの名前空間においていいか悩むクラスも存在します。 その場合、リファクタリングを進めていく中にクラスの機能が絞れてきて 配置すべき名前空間が判ると同時に名前空間を移動したり、 状況によっては名前空間の新設や統廃合を行ったりしてます。 だから「名前」は極めて大事ですね。 クラスの名前を決めるだけで、半日悩んだりします。
分類:[.NET]
VB.NETにおいてクラスライブラリを作成中です。 開発環境は、同一ソリューション内に目的ごとに分けた複数のプロジェクトを 作成し個別のdll化しようと考えています。 プロジェクトの分け方の例で言うと ・ベースプロジェクト[clsBase](基底クラス) 共通使用クラスの定義(構造体の代わりに使用) 列挙型の定義 基本になるメソッドなど ・サブプロジェクト[clsSub](clsBaseからの派生クラス) : : こんな感じです そこで、実際に下記のような構成でプロジェクトを作成し、ユーザクラス上に 派生クラスのインスタンスを作成してみましたが、そのインスタンスから 基底クラスのメソッドなどが参照できません...ちょっと手詰まり状態です。 ※基底・派生クラスが別々のプロジェクトにあるケースのみ参照不可 ・ベースプロジェクト[clsBase](基底クラス) ルート名前空間:baseclass --- clsBase.vb --- Public Class clsBase Public Function GetStringBase() As String Return "GetStringBase()" End Function End Class ・サブプロジェクト[clsSub](clsBaseからの派生クラス) ベースプロジェクトを参照追加 ルート名前空間:subclass --- clsSub.vb --- Public Class clsSub Inherits baseclass.clsBase Public Function GetStringSub() As String Return "GetStringSub()" End Function End Class Public Class clsOverSub2 Inherits clsSub Public Function GetStringOverSub2() As String Return "GetStringOverSub2()" End Function End Class --- clsOverSub.vb --- Public Class clsOverSub Inherits clsSub Public Function GetStringOverSub() As String Return "GetStringOverSub()" End Function End Class ・ユーザプロジェクト[clsUser](通常のクラスライブラリプロジェクト) サブプロジェクトを参照追加 ルート名前空間:userclass --- UserClass.vb --- Imports subclass Public Class UserClass Public Function aaa() As String Dim szStr As String Dim sc As New subclass.clsSub() Dim soc As New subclass.clsOverSub() ----- clsSub.vb内に記述した派生クラス Dim soc2 As New subclass.clsOverSub2() ----- clsSub.vbとは別のclsOverSub.vb内に記述した派生クラス szStr = sc.GetStringBase() & vbCrLf ----- 基底クラスメソッド参照不可 szStr = sc.GetStringSub() & vbCrLf ----- 派生クラスメソッド参照:○ szStr = soc.GetStringSub() & vbCrLf ----- 基底クラスメソッド参照:○ szStr = soc.GetStringOverSub() & vbCrLf ----- 派生クラスメソッド参照:○ szStr = soc2.GetStringSub() & vbCrLf ----- 基底クラスメソッド参照:○ szStr = soc2.GetStringOverSub2() & vbCrLf ----- 派生クラスメソッド参照:○ Return szStr End Function End Class ここで、基底クラスと派生クラスの名前空間の違いの問題?かとも思い 派生クラス側のプロジェクトのルート名前空間をbaseclassに変えてみましたが、 同じ結果でした(参照設定は適宜変更) 尚、基底クラスと派生クラスを同一プロジェクト内に作成すると ユーザクラスで作成したインスタンスで基底クラス・派生クラスの両方の メソッドが参照できました。 しかし、それぞれを別のDLLで管理したいため、プロジェクトは分けた方向で考えたいので困っております たぶん、基本中の基本のレベルのような気もしますが...