C# 使用策略模式提供的构造函数初始化泛型类型

C# initialize a generic type using a strategy-pattern-provided constructor

我有一个在通用类型上运行的 class:

public class Operation<I> where I : IAnimal

我定义 IAnimal 如下:

public interface IAnimal
{
    string Name { get; }
}

我定义一个class如下:

public class Dog : IAnimal
{
    string Name { get; private set; }

    public Dog(string name)
    {
        Name = name;
    }
}

如果我想在Operationclass中使用Dog,因为Dogclass没有无参构造函数,我杠杆策略模式如下:

public interface IConstructor
{
    IAnimal Construct(string name);
}

public class DogConstructor : IConstructor
{
    IAnimal Construct(string name)
    {
        return new Dog(name);
    }
}

public class Operation<I> where I : IAnimal
{
    public Operation() : this(new DogConstructor())
    { }

    public Operation(IConstructor constructor)
    {
        I animal = constructor.Construct("myDog"); // <<<<<<<< Error here!
    }
}

在市场上,我收到 Cannot implicitly convert type 'IAnimal' to 'I'. An explicit conversion exists (are you missing a cast?)

当然,如果我像 I animal = (I)constructor.Construct("myDog"); 这样的情况,一切正常。但是,我想知道为什么我有 where I : IAnimal 时还需要施法。

问题是无法保证您的 IConstructor 将 return 与 I 相同 class。您可以有一个 Operation<Cat>,然后将 DogConstructor 传递给构造函数。

您也可以通过使 IConstructor 泛化来解决此问题,并使 Operation 构造函数接收 IConstructor<I>

However, I am wondering why do I need to cast while I have where I : IAnimal.

是的,您确实保证 I 将是 IAnimalIAnimal 本身的子类,但是 Construct return 会是什么? Construct 可能 return 与 I 不同的子类。

无论何时使用泛型,都应该记住泛型参数类型是由 class/method 的客户端代码提供的,而不是由 class/method 的客户端代码提供的。如果没有传入参数,您在这里通过使用 DogConstructor 强制 I 成为 Dog。如果您这样做,那么这可能意味着泛型在这里不适合。尝试删除它:

public class Operation
{
    public Operation() : this(new DogConstructor())
    { }

    public Operation(IConstructor constructor)
    {
        IAnimal animal = constructor.Construct("myDog");
    }
}

现在如果你坚持使用泛型,你不能假设默认是 DogConstructorIConstructor 也应该是泛型:

public interface IConstructor<T> where I: IAnimal
{
    T Construct(string name);
}

public class DogConstructor : IConstructor<Dog>
{
    Dog Construct(string name)
    {
        return new Dog(name);
    }
}

public class Operation<I> where I : IAnimal
{
    public Operation(IConstructor<I> constructor)
    {
        I animal = constructor.Construct("myDog");
    }
}

public class DogOperation: Operation<Dog> {
    public DogOperation() : base(new DogConstructor()) {}
}

since the Dog class does not have a parameter-less constructor

另一种解决方案可能是约束 I 使其必须具有无参数构造函数,并向 Dog:

添加一个
class Operation<I> where I : IAnimal, new() {

我遇到了同样的问题,我使用了 Activator.CreateInstance(Type type, params object[] args)

static void Main()
{
    var operation = new Operation<Dog>("Jack");
    Console.WriteLine(operation.Animal.Name);
    Console.ReadLine();
}

public interface IAnimal
{
    string Name { get; }
}

public class Dog : IAnimal
{
    public string Name { get; private set; }

    public Dog()
    {
    }

    public Dog(string name)
    {
        Name = name;
    }
}

public class Operation<T> where T : IAnimal, new()
{
    public T Animal { get; private set; }

    public Operation()
    {
        Animal = new T();
    }

    public Operation(params object[] args)
    {
        Animal = (T)Activator.CreateInstance(typeof(T), args);
    }
}

问题是编译器不知道你要传递什么 Class 来代替 I。假设你创建另一个 class Cat 派生自类似于 Dog 的 Animal。现在您在 Operation 中传递 Cat 来代替 I,这根据代码是没问题的。但是 constructor.Construct("myDog") 返回的 Dog 是 Cat 的兄弟,不能解析为 Cat。所以错误会来。见代码

public interface IAnimal
{
        string Name { get; }
}

public class Dog : IAnimal
{
        public string Name { get; set; }

        public Dog(string name)
        {
            this.Name = name;
        }
}

public class Cat : IAnimal
{
        public string Name { get; set; }

        public Cat(string name)
        {
            this.Name = name;
        }
}

public class Operation<I> where I : IAnimal
{
        public Operation() : this(new DogConstructor())
        { }

        public Operation(IConstructor constructor)
        {
            I animal = constructor.Construct("myDog"); // <<<<<<<< Error here!
        }
}

检查下面的代码。您正在传递 Cat 并且希望将其与 Dog 映射。那行不通。

public class XYZ
{
       public void MyMethod()
       {
            var obj = new Operation<Cat>();
       }
}

如果你知道它会 constructor.Construct("myDog") returns 动物然后用 IAnimal 替换 I。这样编译器就可以确定要设置 constructor.Construct("myDog") 的返回对象的引用

public class Operation<I> where I : IAnimal
{
        public Operation() : this(new DogConstructor())
        { }

        public Operation(IConstructor constructor)
        {
            IAnimal animal = constructor.Construct("myDog"); // <<<<<<<< Error here!
        }
}