Зачем использовать общие ограничения в C # - PullRequest
28 голосов
/ 02 ноября 2010

Я прочитал отличную статью на MSDN, касающуюся Generics в C #.

Вопрос, который возник в моей голове, был - почему я должен использовать общие ограничения?

Например, еслиЯ использую такой код:

public class MyClass<T> where T : ISomething
{
}

я не могу поменять ВСЕ ссылки T в этом классе на ISomething?

В чем преимущество использования этого подхода?

Ответы [ 7 ]

47 голосов
/ 02 ноября 2010

Вы спрашиваете: "Разве я не могу переключить ВСЕ ссылки T в этом классе с ISomething?"Поэтому я думаю, что вы хотите сравнить:

public class MyClass<T> where T : ISomething 
{ 
    public T MyProperty { get; set; }
}

с:

public class MyClass 
{
    public ISomething MyProperty { get; set; }
}

Во втором примере MyProperty гарантированно будет только экземпляром ISomething.В первом примере MyProperty - это то, чем является T, даже если это определенный подтип ISomething.Рассмотрим конкретную реализацию ISomething:

public class MySomething : ISomething
{
    public string MyOtherProperty { get; set; }
}

Теперь, если мы используем первый, обобщенный пример, мы могли бы иметь:

MyClass<MySomething> myClass = new MyClass<MySomething>();
Console.WriteLine(myClass.MyProperty.MyOtherProperty);

С другой стороны, если мыВо втором примере мы не смогли бы получить доступ к MyOtherProperty, поскольку известно, что это только ISomething:

MyClass myClass = new MyClass();
Console.WriteLine(myClass.MyProperty.MyOtherProperty); // Won't compile, no property "MyOtherProperty"

С другой стороны, причина того, что эти ограничения типа полезны, состоит в том, чтоВы можете обратиться к MyProperty (введите T) и получить доступ к элементам ISomething.Другими словами, если ISomething было объявлено как:

public interface ISomething 
{
    public string SomeProperty { get; set; }
}

Тогда вы можете получить доступ к MyProperty.SomeProperty.Если вы опустите where T : ISomething, то вы не сможете получить доступ к SomeProperty, поскольку известно, что T будет иметь тип object.

8 голосов
/ 02 ноября 2010

Тип безопасности. Например, предположим, что вы создаете контейнер. Вы можете передать что-то в этот контейнер и получить его в правильной форме, не выполняя каких-либо преобразований позже, путем параметризации контейнера. Вы просто определяете ограничения на типы вещей, которые вы хотите хранить в своем контейнере.

4 голосов
/ 02 ноября 2010

Вот пример разницы, просто используя List<>

Список изображений не был бы универсальным, он просто использовал бы IListElement везде, где вместо него использовался универсальный. Теперь представьте, что у вас есть объект, похожий на этот.

class Element : IListElement
{
   public string Something { get; set; }
}

теперь я мог бы просто сделать list.Add(element);, и не было бы разницы с реальным List<Element>. Однако, когда я получаю данные, это другая история, если я использую список, который использует IListElement, тогда я должен отбросить свои данные обратно, чтобы я мог извлечь Something из них. Таким образом, я должен сделать:

string s = ((Element)list[0]).Something;

пока с универсальным я могу просто сделать:

string s = list[0].Something;

избавляет от многих неприятностей, конечно, это идет немного дальше, но я думаю, что вы можете понять эту идею.

2 голосов
/ 02 ноября 2010

Для начала, вы можете вызывать методы, определенные в ISomething внутри кода, для универсального метода / методов универсального класса.Если бы T было разрешено иметь любой тип, то это было бы невозможно (хотя вы всегда могли бы выполнить некоторое приведение во время выполнения).

Таким образом, это позволяет вам устанавливать ограничения во время компиляции на то, чем может быть T, и поэтому полагатьсяэти ограничения при написании кода - превращение ошибок времени выполнения в ошибки времени компиляции.

1 голос
/ 02 ноября 2010

Да, вы можете использовать ISomething вместо T, но это вручную закроет универсальный тип для обычного класса. Это больше не будет универсальным типом. Используя T, вы сохраняете тип open для любого количества подтипов ISomething, сколько хотите. Повторное использование кода без ущерба для безопасности типов является ключевым преимуществом здесь. Например, если вы используете стек ISomethings, вы можете поместить любое ISomething в стек, но должно произойти всплытие с понижением до фактического подтипа Что-то для того, чтобы это было полезно. Понижение рейтинга создает потенциальную точку отказа, которой не будет в общем Stack<T>, где T: ISomething

0 голосов
/ 06 апреля 2014

Это видео на YouTube на самом деле демонстрирует важность общих ограничений https://www.youtube.com/watch?v=GlqBRIgMgho.

Теперь ниже приводится длинный текстовый ответ.

«Обобщение помогает отделить логику оттип данных.Таким образом, мы присоединяем любой тип данных с любой логикой для высокой степени повторного использования ».

Но часто некоторые логики могут быть привязаны только к определенным типам данных.

public class CompareNumeric<UNNKOWDATATYPE>
{
        public bool Compareme(UNNKOWDATATYPE v1, UNNKOWDATATYPE v2)
        {
            if (v1 > v2)
            {return true;}
            else
           {return false;}
        }
}

Например, выше приведен простой обобщенный класс, который выполняет сравнение, если одно число больше другого числа.Теперь сравнение больше и меньше очень специфично для числовых типов данных.Подобные сравнения не могут быть выполнены для нечисловых типов, таких как строка.

Так что, если некоторые используют классы с типом «int», это совершенно допустимо.

CompareNumeric<int> obj = new CompareNumeric<int>();
bool boolgreater = obj.Compare(10,20);

Если кто-то использует его с«Двойной» тип данных снова совершенно корректен.

CompareNumeric<double> obj = new CompareNumeric<double>();
bool boolgreater = obj.Compare(100.23,20.45);

Но использование строкового типа данных с этой логикой приведет к нежелательным результатам.Поэтому мы хотели бы ограничить или наложить ограничение на то, какие типы могут быть присоединены к универсальному классу. Это достигается с помощью «универсальных ограничений».

CompareNumeric<string> obj = new CompareNumeric<string>();
bool boolgreater = obj.Compare(“interview”,”interviewer”);

Универсальный тип может быть ограничен указанием типа данныхиспользуя ключевое слово «WHERE» после универсального класса, как показано в приведенном ниже коде.Теперь, если какой-либо клиент попытается присоединить «строковый» тип данных с помощью приведенного ниже класса, он не допустит этого, что позволит избежать нежелательных результатов.

public class CompareNumeric<UNNKOWDATATYPE> where UNNKOWDATATYPE : int, double
{

}
0 голосов
/ 02 ноября 2010

Потребитель вашего класса получает выгоду от повышенной безопасности типов, среди прочего.

class Widget : IPokable { }

// No generics
Widget w = (Widget)list[0]; // cast can fail

// With generics
Widget w = list[0];

Без шаблонов, если в списке содержалось IPokable объектов, приведение еще необходимо .

Класс, который вы реализуете, получает выгоду от использования определенных методов для универсального объекта.

class PokableList<T> where T : IPokable {
    public T PokeAndGet() {
        currentObj.Poke();
        return currentObj;
    }
}
...