2010-11-01 3 views
28

Я прочитал отличную статью о MSDN в отношении Generics в C#.Зачем использовать общие ограничения в C#

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

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

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

я не могу переключить все ссылки T в этом классе с ISomething?

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

+0

http://msdn.microsoft.com/en-us/library/d5x73970.aspx – zebrabox

ответ

47

Вы спрашиваете: «Не могу ли я переключить ВСЕ ссылки 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.

2

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

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

7

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

4

Вот пример разницы, только с помощью 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; 

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

1

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

0

Потребитель вашего класса пользуется повышенным уровнем безопасности.

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; 
    } 
} 
0

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

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

«Generic's помогает отделить логику от типа данных. Так что мы прикрепить любой тип данных с какой-либо логики для высокого повторного использования.»

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

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

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

Так что если кто-то использует классы с типом «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» ключевое слово после того, как общий класс, как показано в приведенном ниже коде. Теперь, если какой-либо клиент пытается подключить «строковый» тип данных с указанным ниже классом, он не позволит, что позволяет избежать нежелательных результатов.

Смежные вопросы