Почему мы не можем использовать закрытые классы как общие ограничения? - PullRequest
20 голосов
/ 22 декабря 2009

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

Ответы [ 4 ]

39 голосов
/ 22 декабря 2009

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

Вот код для этого.

public class A
{
    public A() { }
}

public sealed class B : A
{
    public B() { }
}

public class C<T>
        where T : B
{
    public C() { }
}

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

В дополнение к этому у вас также не может быть статического класса в качестве общего ограничения типа. Причина проста. Статические классы помечены как аннотация и запечатаны в скомпилированных IL , которые не могут быть ни созданы, ни наследованы.

Вот код для этого.

public class D<T>
        where T : X
{
    public D() { }
}

public static class X
{
}

Это даст ошибку компилятора: 'X': статические классы не могут быть использованы как ограничения.

5 голосов
/ 22 декабря 2009

Вы говорите о чем-то вроде этого:

class NonSealedClass
{
}

class Test<T> where T : NonSealedClass
{
}

Потому что это совершенно законно.

3 голосов
/ 07 июля 2010

Честно говоря, я не совсем вижу в этом смысл.

Поскольку this .__ curious_geek указывает в своем ответе , запечатанный класс не может быть унаследован, поэтому его использование в качестве ограничения может показаться бессмысленным.

Но нет никакой гарантии, что запечатанный класс никогда не будет «незапечатан», т. Е. Что разработчик может изменить свою реализацию, чтобы сделать его более пригодным для наследования, а затем удалить модификатор sealed из определения класса (или просто категорически удалите ключевое слово sealed без всякой причины).

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

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

Я, наверное, что-то упустил, хотя. Я уверен, что Эрик Липперт может дать довольно убийственное объяснение.

1 голос
/ 22 декабря 2009

Голое ограничение - это когда один универсальный тип наследуется от другого, например,

where X:Y

Один универсальный параметр наследуется от другого универсального параметра

class Foo<T>
{
    Foo<S> SubsetFoo<S>() where S : T {  }
}

Так что класс не может быть запечатан.

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

...