С# generics - что получается, создавая общий класс обертки?

Рассмотрим этот общий класс:

public class Request<TOperation> 
    where TOperation : IOperation
{
    private TOperation _operation { get; set; }

    public string Method { get { return _operation.Method; } }

    public Request(TOperation operation)
    {
        _operation = operation;
    }
}

Какую реальную выгоду предлагает более общая версия выше этой не-общей версии ниже?

public class Request
{
    private IOperation _operation { get; set; }

    public string Method { get { return _operation.Method; } }

    public Request(IOperation operation)
    {
        _operation = operation;
    }
}

Интерфейс IOperation:

public interface IOperation
{
    string Method { get; }
}

Ответ 1

В стандартной версии метод может принимать параметр типа Request<FooOperation>. Передача в экземпляре Request<BarOperation> недействительна.
Таким образом, общая версия позволяет методам обеспечить получение запроса на правильную работу.

Ответ 2

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

Ответ 3

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

public class Foo<T> 
    where T : IComparable
{
    private T _inner { get; set; }

    public Foo(T original)
    {
        _inner = original;
    }

    public bool IsGreaterThan<T>(T comp)
    {
        return _inner.CompareTo(comp) > 0;
    }
}

против

public class Foo             
    {
        private IComparable  _inner { get; set; }

        public Foo(IComparable original)
        {
            _inner = original;
        }

        public bool IsGreaterThan(IComparable  comp)
        {
            return _inner.CompareTo(comp) > 0;
        }
}

Если у вас тогда был Foo<int>, вы, вероятно, не хотели бы сравнивать его с Foo<string>, но не могли бы заблокировать это, используя не-общую версию.

Ответ 4

Например, если у вас

public class SubOperation : IOperation 
{
    // implementation ...
}

и

public class OtherSubOperation : IOperation 
{
    // implementation ...
}

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