【问题标题】:c# Factory for concrete implementation of generic base classc# Factory 用于泛型基类的具体实现
【发布时间】:2016-04-27 12:46:51
【问题描述】:

我有一个通用的基类。 我有一个实现基类的具体类。

如何创建工厂类/方法来交付不同类型的具体类?

这里是一个例子:

public class ReceiverBase<T>
    where T : IInterpreter
{ ... }

public class SpecialReceiver : ReceiverBase<OwnInterpreter> { ... }

public class ReceiverFactory<T>
    where T : ReceiverBase<IInterpreter>, new()

    public T Create(string type) {
        switch(type) {
            default:
                return new SpecialReceiver();
        }
    }
}

问题在于 ReceiverBase 似乎不可能,因为编译器只希望类作为约束,而不是接口。 第二个问题是我无法将 SpecialReceiver 转换为 T。

那么有没有办法让它工作?

=== 编辑:根据第一个答案添加示例 ===

public interface IInterpreter
{
}

public class OwnInterpreter : IInterpreter
{
    public void Dispose()
    {
        throw new NotImplementedException();
    }

    public void DoSomething() { }
}



public abstract class ReceiverBase<T>
    where T : IInterpreter
{
    public T MyReceiver { get; set; }

    internal abstract void Start();
}

public class SpecialReceiver<T> : ReceiverBase<T>
    where T : IInterpreter, new()
{
    public void CheckSomething()
    {
        MyReceiver.DoSomething();
    }

    internal override void Start()
    {
        MyReceiver = new T();
    }
}

public class ReceiverFactory<T>
    where T : IInterpreter, new()
{
    public static ReceiverBase<T> Create(string type)
    {
        switch (type)
        {
            default:
                return new SpecialReceiver<T>();
        }
    }
}

问题是:MyReceiver.DoSomething();不管用。 此外,我必须像这样给工厂打电话:ReceiverFactory&lt;OwnInterpreter&gt;.Create(""); 我想这样:ReceiverFactory.Create("SpecialReceiver");

【问题讨论】:

  • 您的第一条评论根本不是真的。您当然可以将接口作为约束。但是,您可能会遇到的是,因为您希望能够 new up 受约束类型的实例,所以您必须像您一样包含 new() 约束。
  • Compiler 说:IInterpreter 类型必须有一个公共的无参数构造函数才能将其用作参数 T。Compiler 引用此行: where T : ReceiverBase, new()
  • 好的,我更正了。您可以将接口作为约束,但在这种情况下您不能,因为您想使用new(),并且您必须保证所述构造函数存在。接口不保证构造函数。
  • 是的,你是对的。 new() 约束是第一个错误的问题。

标签: c# generics factory


【解决方案1】:

您可以在工厂中使用泛型方法:

class Program
{
    static void Main(string[] args)
    {
        var own = ReceiverFactory.Create<OwnInterpreter>();
        var other = ReceiverFactory.Create<OtherInterpreter>();
        own.Start();
        other.Start();
        Console.ReadLine();
    }
}
interface IInterpreter
{
    void DoSomething();
}

class OwnInterpreter : IInterpreter
{
    public void DoSomething() { Console.WriteLine("Own"); }
}

class OtherInterpreter : IInterpreter
{
    public void DoSomething() { Console.WriteLine("Other"); }
}

abstract class ReceiverBase<T> where T: IInterpreter, new()
{
    public T Interpreter { get; set; }
    public ReceiverBase()
    {
        Interpreter = new T();  
    }
    public void Start()
    {
        Interpreter.DoSomething();
    }
}

class SpecialReceiver : ReceiverBase<OwnInterpreter> { }
class OtherReceiver : ReceiverBase<OtherInterpreter> { }

static class ReceiverFactory
{
    private static Dictionary<string, object> factories = new Dictionary<string, object>();
    static ReceiverFactory()
    {
        RegisterFactory(() => new SpecialReceiver());
        RegisterFactory(() => new OtherReceiver());
    }
    public static void RegisterFactory<T>(Func<ReceiverBase<T>> factory) where T : IInterpreter, new()
    {
        factories.Add(typeof(T).FullName, factory);
    }
    public static ReceiverBase<T> Create<T>() where T : IInterpreter, new()
    {
        var type = typeof(T);
        return ((Func<ReceiverBase<T>>)factories[type.FullName]).Invoke();
    }
}

其实这里不需要“new()”约束,因为你使用的是工厂。

【讨论】:

  • 感谢您提供的精美示例,但这里有一个普遍的问题。我不想区分解释器,而是区分接收器类。在我的设计问题中,我有不同类型的信息来源,它们是相同的。就我而言,这是一个位置。 (GPS、小区等)每个来源都提供纬度和经度。我现在想要一个提供坐标的工厂。我只想用 Create("GPS") 或 Create("Cell") 或其他任何能提供此类信息的东西来调用工厂。
【解决方案2】:

我建议您将代码更改为:

    public class ReceiverBase<T> where T : IInterpreter
    {
    }

    public interface IInterpreter
    {
    }

    public class SpecialReceiver<T> : ReceiverBase<T>
        where T : IInterpreter
    {
    }

    public class OwnInterpreter : IInterpreter
    {
    }

    public class ReceiverFactory<T> where T : IInterpreter, new()
    {
        public ReceiverBase<T> Create(string type)
        {
            switch (type)
            {
                default:
                    return new SpecialReceiver<T>();
            }
        }
    }

您不能只返回T 的原因是SpecialReceiverReceiverBase&lt;IInterpreter&gt; 之间没有隐式转换。

【讨论】:

  • 这会导致另外两个问题:我无法在使用 OwnInterpreter 实例的 SpecialReceiver 中实现代码。我需要用具体的 IInterpreter 给工厂打电话。在这种情况下,它将是 OwnInterpreter。工厂应该只需要一个可以从配置或其他地方读取的字符串参数。
  • @DARKHalf 是哪个? :)
  • 编辑了我原来的问题
【解决方案3】:

我能够找到适合我需要的解决方案。 我添加了另一个接口 IReciver,它定义了我真正需要的属性和成员。工厂方法返回 IReceiver,因此我可以省略泛型的所有绑定问题。有时就是这么简单。 :)

public interface IInterpreter { }

public interface IReceiver
{
    bool Enabled { get; set; }
}

public class OwnInterpreter : IInterpreter
{
    public void DoSomething() { }
}

public abstract class ReceiverBase<T> : IReceiver
    where T : IInterpreter, new()
{
    public T MyReceiver { get; set; }

    internal abstract void Start();

    private bool _isEnabled;
    public bool Enabled { get { return _isEnabled; } set { _isEnabled = value; OnEnable(value); } }

    internal abstract void OnEnable(bool isEnabled);

    protected ReceiverBase()
    {
        MyReceiver = new T();
    }
}

public class SpecialReceiver : ReceiverBase<OwnInterpreter>
{
    public void CheckSomething()
    {
        MyReceiver.DoSomething();
    }

    internal override void Start()
    {
        // just for testing puropses
        MyReceiver = new OwnInterpreter();
    }

    internal override void OnEnable(bool isEnabled)
    {
        MyReceiver = isEnabled ? new OwnInterpreter() : null;
    }
}

public class ReceiverFactory
{
    public static IReceiver Create(string type)
    {
        switch (type)
        {
            default:
                return new SpecialReceiver();
        }
    }
}

public class Program
{
    [STAThread]
    public static void Main()
    {
        ReceiverFactory.Create("");
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-07-11
    • 2018-09-20
    • 2019-03-19
    • 1970-01-01
    • 2010-10-22
    • 2013-10-06
    • 1970-01-01
    相关资源
    最近更新 更多