Какова цель ограничения типа обобщенного метода?

Мне сложно понять, почему было бы полезно сделать что-то вроде этого: (Образец - это класс)

static void PrintResults<T>(T result) where T : Sample

Не было бы лучше просто передать Sample в метод?

 static void PrintResults (Sample result)

Ответы

Ответ 1

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

Например, указывая тип возврата в общем случае:

static T Create<T>() where T: Sample, new()
{
    return new T();
}

// Calling code
Sample sample = Create<Sample>();

вместо

static object Create()
{
    return new Sample();
}

// Calling code
Sample sample = (Sample) Create();

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

static T Create<T>() where T: IMyInterface, new()
{
    return new T();
}

interface IMyInterface {} 
class MyClass : IMyInterface { }

// Calling code.
MyClass myClass = Create<MyClass>();

Это позволяет генерировать новый тип, который реализует определенный интерфейс и имеет общий конструктор. Также:

static void DoSomething<T>(T t) where T: IMyInterface1, IMyInterface2
{
    t.MethodOnIMyInterface1();
    t.MethodOnIMyInterface2();
}

interface IMyInterface1 
{
    void MethodOnIMyInterface1();
}     
interface IMyInterface2
{
    void MethodOnIMyInterface2();
}     
class MyClass: IMyInterface1, IMyInterface2 
{ 
    // Method implementations omitted for clarity
}

// Calling code
MyClass myclass'
DoSomething(myclass);  // Note that the compiler infers the type of T.

Если вы можете потребовать несколько интерфейсов по одному параметру без (1) создания нового типа, который реализует все эти интерфейсы, и (2) требующих наличия параметров этого типа.

Как отмечает @dcastro в своем ответе, общие типы также могут сказать, что компилятор требует, чтобы типы были одинаковыми. Например:

static void DoSomething<T>(T t1, T t2) where T: MyType
{
    // ...
}

class MyType {}
class MyType1: MyType {} 
class MyType2: MyType {} 

// Calling code
MyType1 myType1;
MyType2 myType2;
DoSomething<MyType>(myType1, myType2);

Если компилятор требует, чтобы t1 и t2 были одного типа, но могут быть любыми типами, наследующими MyType. Это полезно в системах автоматизированного модульного тестирования, таких как NUnit или MSTest, для общих проверок равенства и сравнения.

Ответ 2

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

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

Ответ 3

В пустотах вы всегда можете использовать интерфейс в качестве параметра, чтобы работать с несколькими типами, поэтому здесь часто полезны generics arent.

Только исключения - это ограничения для генериков. И этим я не имею в виду что-то вроде   где T: IA, IB так как это может быть сделано с помощью интерфейса, а также реализует IA и IB. В какой-то момент это будет утомительно, так как вам понадобится больше и больше интерфейсов. Поэтому давайте рассмотрим класс "специальные ограничения" и новый

public void AddNew(List<T> items) where T : new
{
    items.Add(new T());
}

и класс, который полезен, если метод мутирует его параметр, который не работает для structs

static void IncA<T>(T item) where T: class, IA
{
    item.A++;
}

Реальная мощность дженериков заключается в том, что методы имеют общий тип возвращаемого значения или общие классы, такие как List <T> . Вы не хотите внедрять новый класс для каждого списка, который вам нужен.