[英]How do I write an interface or abstract class that specifies creation logic?
我有一个通用类,涉及可以从字符串反序列化的小部件。 通用类的实例将这些小部件之一的类型作为模板参数,然后从字符串创建这些小部件。 我希望使用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
来完成几乎相同的事情。 这是功能性的,但是我怀疑还有一种更直接的方法不会强迫我定义一个虚拟类。 如何在不创建伪类或额外工厂的情况下将意图传达给类型系统。 我只想要一个说“您可以从字符串中制作一个”的界面。
TL; DR:是否可以通过某种方法编写接口或抽象类,使我可以从字符串创建实例,但不需要我以new()
作为WidgetUser<T>
的保护WidgetUser<T>
?
我将实现WidgetFactory
并调用WidgetFactory.Create<T>(serializedWidget)
以避免使用new T()
这里的问题是您的Deserialize()
方法应该是静态方法。 因此,它不应该是IWidget
本身的成员-它应该是Factory接口的成员,或者应该是具体Widget类的静态成员,而该Widget类是从具体factory方法调用的。 我在下面显示后一种方法。
(或者,您可以使用Func<IWidget>
委托来指定它,但更常见的是提供完整的工厂接口。)
因此,我建议您创建工厂界面:
interface IWidgetFactory
{
IWidget Create(string serialisedWidget);
}
然后从IWidget
删除Deserialize()
:
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;
}
请注意,在这种情况下,您不再需要WidgetUser
的type参数,因此已将其删除。
然后,当您创建WidgetUser
您必须提供一个工厂:
var widgetUser = new WidgetUser(new MyWidgetFactory());
...
widgetUser.MakeAndUse("MySerializedWidget1");
widgetUser.MakeAndUse("MySerializedWidget2");
通过工厂可带来更大的灵活性。
例如,假设您的序列化方案包括一种从序列化的字符串中分辨出它是哪种小部件的方法。 为了简单起见,假设它是"[MyWidget]"
则以"[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之类的依赖容器来管理此类操作。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.