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

问题描述 投票:0回答:4

我有一个在泛型类型上运行的类:

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

c# generics constructor strategy-pattern
4个回答
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() {

2
投票

问题是编译器不知道你要通过什么类来代替我。假设你创建了另一个类派生自Cat的类似于Dog的类。现在你将Cat in Operation代替我,这很好,按照代码。但构造函数.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”)返回动物然后用IAnimal替换我。这样编译器就可以确定要从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!
        }
}

1
投票

问题是,不能保证你的IConstructor将返回与I相同的类。你可以有一个Operation<Cat>,并传递给构造函数DogConstructor

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


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);
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.