Подтвердить что ты не робот

Зачем использовать общие ограничения в С#

Я прочитал отличную статью о MSDN в отношении Generics в С#.

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

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

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

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

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

4b9b3361

Ответ 1

Вы спрашиваете: "Не могу ли я переключить ВСЕ ссылки 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 (type T) и получить доступ к элементам ISomething. Другими словами, если ISomething были объявлены как:

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

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

Ответ 2

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

Ответ 3

Здесь пример разницы, просто используя 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;

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

Ответ 4

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

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

Ответ 5

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

Ответ 6

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

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

Ответ 7

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

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

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

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

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

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

}