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

时间:2018-09-06 05:40:11

标签: c# generics constructor strategy-pattern

我有一个对通用类型进行操作的类:

public class Operation<I> where I : IAnimal

并且我将IAnimal定义如下:

public interface IAnimal
{
    string Name { get; }
}

我将类定义如下:

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

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

如果我想在Dog类中使用Operation,则由于Dog类没有无参数构造函数,因此我将策略模式用于以下方面:

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时需要投射。

4 个答案:

答案 0 :(得分:2)

  

但是,我想知道为什么我在I : IAnimal所在的位置时需要进行投射。

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

无论何时使用泛型,都应记住泛型参数类型是由类/方法的客户端代码提供的,而不是类/方法。在这里,如果没有传递参数,则通过使用I强制Dog成为DogConstructor。如果执行此操作,则可能意味着泛型在这里不合适。尝试删除它:

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

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

现在,如果您坚持使用泛型,则不能默认使用DogConstructor,而IConstructor也应该是泛型的:

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()) {}
}
  

因为Dog类没有无参数的构造函数

另一种解决方案可能是约束I,使其必须具有无参数构造函数,然后将其添加到Dog

class Operation<I> where I : IAnimal, new() {

答案 1 :(得分:2)

问题是编译器不知道您要代替I传递什么类。假设您创建另一个类,该类从Animal派生而来,类似于Cat。现在,您将Cat代替I传递给了Operation,按照代码可以。但是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>();
       }
}

如果您知道它将构造方法。Construct(“ myDog”)返回animal,然后将I替换为IAnimal。这样,编译器就可以确定要在其中设置从构造函数.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!
        }
}

答案 2 :(得分:1)

问题在于无法保证您的**Table Online** Col1 Col2 Score | a b 1 | a c 2 | a d 3 | f e 4 | **Table Offline** Col1 Col2 Score | a m 10 | a c 20 | a d 30 | t k 40 | **Table Output** Col1 Col2 Online.Score Offline.Score | a c 2 20 | a d 3 30 | a b 1 | a m 10 | 返回与IConstructor相同的类。您可以拥有一个I,并将一个Operation<Cat>传递给构造函数。

您也可以通过使DogConstructor通用来解决此问题,并使Operation构造函数接收IConstructor

答案 3 :(得分:1)

我遇到了同样的问题,并且我使用了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);
    }
}