C # инициализирует универсальный тип, используя конструктор, предоставленный шаблоном стратегии - PullRequest
0 голосов
/ 06 сентября 2018

У меня есть класс, который работает с универсальным типом:

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 голосов
/ 06 сентября 2018

Проблема в том, что компилятор не знает, какой класс вы собираетесь передать вместо I. Предположим, вы создаете другой класс Cat, производный от Animal, похожий на Dog. Теперь вы передаете Cat в Operation вместо 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!
        }
}

Проверьте код ниже. Вы передаете Кота, и вы хотите, чтобы это было сопоставлено с Собакой. Это не могло сработать.

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

Если вы знаете, что это будет constructor.Construct ("myDog") возвращает animal, тогда замените I на 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!
        }
}
0 голосов
/ 06 сентября 2018

Однако мне интересно, почему мне нужно разыграть, когда у меня есть где I : IAnimal.

Да, вы гарантировали, что I будет подклассом IAnimal или IAnimal, но что вернет 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() {
0 голосов
/ 06 сентября 2018

Я столкнулся с той же проблемой, и я использовал Activator.CreateInstance (Тип типа, объект params [] 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);
    }
}
0 голосов
/ 06 сентября 2018

Проблема в том, что нет гарантии, что ваш IConstructor вернет тот же класс, что и I. Вы можете получить Operation<Cat> и передать конструктору DogConstructor.

Вы можете решить эту проблему, сделав также универсальный IConstructor, и сделав конструктор Operation для получения IConstructor<I>.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...