Проверка универсального параметра во время компиляции

#c# #generics

#c# #универсальные

Вопрос:

Допустим, у меня есть следующий класс:

 public class MyClass<T>
{
    public MyClass() {
        //construct logic
    }

    public IProperty<T> MyProperty { get; set; }
}

public interface IProperty<T> { }

public DateTimeProperty : IProperty<DateTime>
{

}
  

Если я попытаюсь поместить этот код в конструктор MyClass :

 if (typeof(T) == typeof(DateTime))
    MyProperty = new DateTimeProperty();
  

Я получаю следующую ошибку компиляции:

Не удается неявно преобразовать тип ‘DateTimeProperty’ в ‘iProperty’

Как я могу в конструкторе MyClass установить значение MyProperty равным a new DateTimeProperty , только если T имеет тип DateTime ?

Комментарии:

1. Вы пробовали преобразовать его явно?

2. Использование этой конструкции на самом деле не предназначено для использования дженериков.

Ответ №1:

Обратите внимание, что дженерики действительно должны быть именно такими; универсальными; то, что вы делаете, является специализированным (для некоторых T ), что в некотором роде не имеет значения. Однако, что касается как — приведение:

 MyProperty = (IProperty<T>)(object)new DateTimeProperty();
  

Приведение к object , а затем резервное копирование на (IProperty<T>) вы лишаете компилятор возможности применять статические правила проверки типов. И обратная сторона: вы лишаете компилятор возможности применять статические правила проверки типов.

Комментарии:

1. Спасибо за ваш ответ, Марк. Я действительно использую общий параметр во всем этом классе. Ближе к истине то, что у меня есть коллекция IProperty , большинство из которых являются общими, но некоторые из них специфичны для конкретного типа. Есть ли шаблон проектирования, который вы могли бы порекомендовать, который является лучшим способом достижения этого? Возможно, заводской класс для каждого типа?

2. @ConnellWatkins хммм … фабрика звучит как чрезмерное усложнение; постарайтесь сделать это простым, пока вам не понадобится сложность. Все, что работает…

3. Да, я рассматривал фабрики, но думал именно так. Иногда вам просто нужно знать, когда добавление пары не очень рекомендуемых строк кода было бы более продуктивным. Могу ли я создать статический метод, который принимает MyClass<DateTime> объект и устанавливает MyProperty значение new DateTimeProperty() ? Правила статической проверки типов все равно будут применяться здесь, не так ли?

4. @Connell meh, может быть, просто используйте приведение; p Иначе вы просто будете преследовать его в цикле (перемещая проблему в другое место, но на самом деле не изменяя ее)

Ответ №2:

Хотя это всего лишь стилистическое различие, я предпочитаю следующее предложению Марка:

 ((MyClass<DateTime>)this).MyProperty = new DateTimeProperty();
  

Комментарии:

1. Это аккуратнее, мне нравится этот. Я мог бы даже использовать as ключевое слово, чтобы избежать проверки типа, а затем приведения: MyClass<DateTime> dt = this as MyClass<DateTime>; if(dt != null) { dt.MyProperty = new DateTimeProperty(); }

Ответ №3:

Я думаю, вам не следует этого делать, потому что тогда универсальный больше не будет универсальным, как сказал Марк, если у вас есть базовый класс, который реализует все, что вам нужно, и вы хотите ограничить MyClass создание только типами, производными от вашего базового класса, вы можете использовать where (ограничение универсального типа) (ссылка C #):

 public class MyClass<T> where T : YourBaseClass
  

Комментарии:

1.Моя интерпретация заключается в том, что он не делает того, чем все T должно быть ограничено DateTime , а скорее: когда T есть DateTime , примените некоторую специализацию.