如何编写指定创建逻辑的接口或抽象class?

How do I write an interface or abstract class that specifies creation logic?

我有一个通用的 class 来处理可以从字符串反序列化的小部件。通用 class 的实例将这些小部件之一的类型作为模板参数,然后从字符串创建这些小部件。我希望使用 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 的实例(或等效的抽象 class)不能保证与 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 的代码。我可以使用抽象的 class BaseWidget 而不是 IWidget 来完成几乎相同的事情。这是功能性的,但我怀疑有一种更直接的方法不会强迫我定义一个虚拟 class。如何在不创建虚拟 classes 或额外工厂的情况下将我的意图传达给类型系统。我只想要一个显示 "you can make one of these from a string".

的界面

长话短说: 有什么方法可以编写一个接口或抽象 class 让我从字符串创建一个实例但不需要我有 new() 作为 WidgetUser<T> 的守卫?

我会实现 WidgetFactory 并调用 WidgetFactory.Create<T>(serializedWidget) 以避免使用 new T()

这里的问题是您的 Deserialize() 方法应该是静态方法。因此它不应该是 IWidget 本身的成员 - 它应该是工厂接口的成员,或者它应该是从具体工厂方法调用的具体 Widget class 的静态成员。我在下面展示了后一种方法。

(或者,您可以使用 Func<IWidget> 委托来指定它,但更常见的是提供完整的工厂接口。)

所以我建议你创建工厂接口:

interface IWidgetFactory
{
    IWidget Create(string serialisedWidget);
}

然后从 IWidget 中删除 Deserialize():

interface IWidget
{
    // .. Whatever
}

然后在IWidget的每个具体实现中添加静态Deserialize()方法:

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.
}

然后使用具体小部件 class:

中的静态 Deserialize() 方法为具体小部件 class 实现工厂
sealed class MyWidgetFactory : IWidgetFactory
{
    public IWidget Create(string serialisedWidget)
    {
        return MyWidget.Deserialize(serialisedWidget);
    }
}

然后向您的 WidgetUser class 添加一个构造函数,它接受 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 的类型参数,因此我已将其删除。

然后当您创建 WidgetUser 时,您必须提供一个工厂:

var widgetUser = new WidgetUser(new MyWidgetFactory());

...

widgetUser.MakeAndUse("MySerializedWidget1");
widgetUser.MakeAndUse("MySerializedWidget2");

通过工厂允许更多的灵活性。

例如,假设您的序列化方案包括一种从序列化字符串中判断它是哪种小部件的方法。为了简单起见,假设它是 MyWidget 则以 "[MyWidget]" 开头,如果是 MyOtherWidget.

则以 ["MyOtherWidget"] 开头

然后你可以实现一个工厂,作为一个 "virtual constructor",它可以创建任何类型的 Widget,给定一个序列化字符串,如下所示:

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 之类的依赖容器来管理此类事情。