文字列から逆シリアル化できるウィジェットを処理するジェネリッククラスがあります。ジェネリッククラスのインスタンスは、これらのウィジェットの1つのタイプをテンプレートパラメーターとして受け取り、文字列からこれらのウィジェットを作成します。C#のジェネリックの共分散プロパティを使用して、またはでWidgetUser<IWidget>
ある可能性のあるオブジェクトを処理するようなコードを記述したいと思います。問題は、内部の文字列からウィジェットを作成するために、ガードとして追加する必要があることです。これにより、無効な型になります。現在、私は次のようなコードを持っています:WidgetUser<RedWidget>
WidgetUser<BlueWidget>
WidgetUser<T>
new()
WidgetUser<IWidget>
interface IWidget
{
// Makes this widget into a copy of the serializedWidget
void Deserialize(string serializedWidget);
}
class WidgetUser<T> where T : IWidget, new()
{
public void MakeAndUse(string serializedWidget)
{
var widget = new T();
widget.Deserialize(serializedWidget);
Use(widget);
}
}
このコードを使用するWidgetUser<BlueWidget>
と、がBigWidget
満たされるため、問題なく作成できnew()
ます。のWidgetUser<IWidget>
インスタンスIWidget
(または同等の抽象クラス)がで動作することが保証されていないため、私は書くことができませんnew()
。回避策は次のとおりです。
abstract class WidgetUser
{
public abstract void MakeAndUse();
}
class WidgetUser<T> : WidgetUser
where T : IWidget, new()
{
/* same as before but with an 'override' on MakeAndUse */
}
このコードを使用して、を処理するWidgetUser<BlueWidget>
コードを作成して作成できますWidgetUser
。ほぼ同じことを実現するBaseWidget
代わりに、抽象クラスを使用して同様のコードを作成することもできますIWidget
。これは機能的ですが、ダミークラスを定義することを強制しないより直接的なアプローチがあると思います。ダミークラスや追加のファクトリを作成せずに、型システムに意図を伝えるにはどうすればよいですか。「文字列からこれらの1つを作成できます」というインターフェイスが必要です。
TL; DR:文字列からインスタンスを作成できるがnew()
、ガードとして必要としないインターフェイスまたは抽象クラスを作成する方法はありますWidgetUser<T>
か?
ここでの問題は、Deserialize()
メソッドが静的メソッドでなければならないということです。したがって、IWidget
それ自体のメンバーであってはなりません。ファクトリインターフェイスのメンバーであるか、具象ファクトリメソッドから呼び出される具象ウィジェットクラスの静的メンバーである必要があります。後者のアプローチを以下に示します。
(または、Func<IWidget>
デリゲートを使用して指定することもできますが、完全なファクトリインターフェイスを提供するのが一般的です。)
したがって、ファクトリインターフェイスを作成することをお勧めします。
interface IWidgetFactory
{
IWidget Create(string serialisedWidget);
}
次に、Deserialize()
からを削除しIWidget
ます:
interface IWidget
{
// .. Whatever
}
次に、静的Deserialize()
メソッドを次の各具体的な実装に追加しますIWidget
。
class MyWidget: IWidget
{
public static MyWidget Deserialize(string serializedWidget)
{
// .. Whatever you need to deserialise into myDeserializedObject
return myDeserializedObject;
}
// ... Any needed IWidget-implementing methods and properties.
}
次に、具象ウィジェットクラスの静的Deserialize()
メソッドを使用して具象ウィジェットクラスのファクトリを実装します。
sealed class MyWidgetFactory : IWidgetFactory
{
public IWidget Create(string serialisedWidget)
{
return MyWidget.Deserialize(serialisedWidget);
}
}
次に、WidgetUser
を受け入れるコンストラクターをクラスに追加し、それを次の場所IWidgetFactory
で使用しMakeAndUse()
ます。
class WidgetUser
{
public WidgetUser(IWidgetFactory widgetFactory)
{
this.widgetFactory = widgetFactory;
}
public void MakeAndUse(string serializedWidget)
{
var widget = widgetFactory.Create(serializedWidget);
Use(widget);
}
private readonly IWidgetFactory widgetFactory;
}
このシナリオでは、のtype引数は不要になったためWidgetUser
、削除したことに注意してください。
次に、作成するときにWidgetUser
、ファクトリを提供する必要があります。
var widgetUser = new WidgetUser(new MyWidgetFactory());
...
widgetUser.MakeAndUse("MySerializedWidget1");
widgetUser.MakeAndUse("MySerializedWidget2");
工場に渡すことで、より多くの柔軟性が得られます。
たとえば、シリアル化スキームに、シリアル化された文字列からウィジェットの種類を判別する方法が含まれていると想像してください。簡単にする目的のために、それはで始まると仮定し"[MyWidget]"
、それはだ場合MyWidget
とで始まる["MyOtherWidget"]
、それはだ場合MyOtherWidget
。
次に、次のようにシリアル化文字列を指定して任意の種類のウィジェットを作成できる「仮想コンストラクター」として機能するファクトリを実装できます。
sealed class GeneralWidgetFactory: IWidgetFactory
{
public IWidget Create(string serialisedWidget)
{
if (serialisedWidget.StartsWith("[MyWidget]"))
return myWidgetFactory.Create(serialisedWidget);
else if (serialisedWidget.StartsWith("[MyOtherWidget]"))
return myOtherWidgetFactory.Create(serialisedWidget);
else
throw new InvalidOperationException("Don't know how to deserialize a widget from: " + serialisedWidget);
}
readonly MyWidgetFactory myWidgetFactory = new MyWidgetFactory();
readonly MyOtherWidgetFactory myOtherWidgetFactory = new MyOtherWidgetFactory();
}
これは一般的に物事を行うための最良の方法ではないことに注意してください-この種のことを管理するには、Autofacなどの依存関係コンテナを使用する方が良いでしょう。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加