Время от времени я усложняю простой интерфейс, добавляя к нему ограничение параметра самоссылающегося («рефлексивного») типа. Например, я мог бы превратить это:
interface ICloneable
{
ICloneable Clone();
}
class Sheep : ICloneable
{
ICloneable Clone() { … }
} //^^^^^^^^^^
Sheep dolly = new Sheep().Clone() as Sheep;
//^^^^^^^^
в:
interface ICloneable<TImpl> where TImpl : ICloneable<TImpl>
{
TImpl Clone();
}
class Sheep : ICloneable<Sheep>
{
Sheep Clone() { … }
} //^^^^^
Sheep dolly = new Sheep().Clone();
Основное преимущество: тип реализации (такой как Sheep
) теперь может ссылаться на себя, а не на свой базовый тип, что снижает потребность в приведении типов (как показано в последней строке кода).
Хотя это очень приятно, я также заметил, что эти ограничения параметров типа не являются интуитивно понятными и имеют тенденцию становиться действительно трудными для понимания в более сложных сценариях.*)
Вопрос. Кто-нибудь знает другой шаблон кода C#, который обеспечивает тот же эффект или что-то подобное, но более простым для понимания способом?
*) Этот шаблон кода может быть неинтуитивным и трудным для понимания, например. следующими способами:
Объявление
X<T> where T : X<T>
выглядит рекурсивным, и можно задаться вопросом, почему компилятор не застревает в бесконечном цикле, рассуждая "ЕслиT
являетсяX<T>
, тоX<T>
на самом деле являетсяX<X<…<T>…>>
". (Но ограничения, очевидно, не разрешаются таким образом.)Для разработчиков может быть неочевидно, какой тип следует указывать вместо
TImpl
. (Ограничение в конечном итоге позаботится об этом.)Как только вы добавите в смесь больше параметров типа и отношений подтипов между различными универсальными интерфейсами, ситуация довольно быстро станет неуправляемой.
interface ICloneable<T> { T Clone(); }
? - person   schedule 15.01.2012Sheep
какclass Sheep : ICloneable<Dog> { public Dog Clone() {…} }
, что может быть не тем, что изначально имелось в виду с интерфейсомICloneable
. - person stakx - no longer contributing   schedule 15.01.2012class Cow : ICloneable<Sheep>
. - person Krizz   schedule 15.01.2012ICloneable<T>
достаточно. Как показывают ответы ниже, дополнительное ограничение гарантирует, что аргумент типа реализуетICloneable<>
только для некоторого типа, но не обязательно для того же самого типа. - person stakx - no longer contributing   schedule 15.01.2012