Дженерики, использующие общедоступные интерфейсы и параметры внутреннего типа

У меня такая ситуация:

// A public interface of some kind   
public interface IMyInterface {   
    int Something { get; set; }   
}   

// An internal class that implements the public interface.   
// Despite the internal/public mismatch, this works.   
internal class MyInternalConcrete : IMyInterface {   
    public int Something { get; set; }   
}   

// A generic class with an interface-restricted type parameter.
// Note that the constraint on T uses the *public* interface.
// The instance is *never* exposed as a public, or even protected member.
public class MyClass<T> where T : IMyInterface, new() {   
    T myInterfaceInstance;   

    public MyClass() {   
        myInterfaceInstance = new T();   
    }   
}   

// Attempting to implement concrete class... Inconsistent Accessibility Error!   
public class MySpecificClass : MyClass<MyInternalConcrete>   
{   
}  

При попытке реализовать MySpecificClass я получаю сообщение об ошибке:

Несогласованная доступность: базовый класс App1.MyClass менее доступен, чем класс App1.MySpecificT.

Странно то, что MyInternalConcrete, несмотря на то, что он внутренний, все же может реализовать общедоступный интерфейс. А поскольку он реализует интерфейс, то его следует использовать в качестве параметра типа для MyClass, поскольку T ограничен общедоступным интерфейсом, а не внутренним классом.

Я бы понял, что это не сработает, если MyClass предоставит T, так же, как он потерпел бы неудачу, если бы мы не использовали дженерики:

public class MyClass<T> where T : IMyInterface, new() {      
    T myInterfaceInstance;      

    public MyClass() {      
        myInterfaceInstance = new T();      
    }      

    // This will fail with an internal T - inconsistent accessibility!    
    public T Instance {      
        get { return myInterfaceInstance; }      
    }      
}

То же, что и выше, но без дженериков:

public class MyNonGenericClass {   
    MyInternalConcrete myInterfaceInstance;   

    public MyNonGenericClass() {   
        myInterfaceInstance = new MyInternalConcrete();   
    }   

    // This will fail - inconsistent accessibility! 
    // but removing it works, since the private instance is never exposed.   
    public MyInternalConcrete Instance {   
        get { return myInterfaceInstance; }   
    }   
}  

Является ли это ограничением универсальных шаблонов C # или я просто неправильно понимаю что-то фундаментальное о том, как работают универсальные шаблоны?

Я также разместил эту тему на MSDN, но меня увольняют, так как я не понимаю, о чем говорю. Верно ли мое беспокойство?


person Alex J    schedule 31.01.2009    source источник


Ответы (3)


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

C # строго типизирован, поэтому ...

Чтобы иметь возможность ссылаться на MySpecificClass вне области сборки, в которой он определен, вы должны знать его типы параметров, чтобы сгенерировать ссылку строгого типа на его экземпляр; но отдельная сборка, чем внутреннее определение, не знает о MyInternalConcrete.

Таким образом, следующее не будет работать, если в отдельной сборке:

MyClass<MyInternalConcrete> myInstance = new MySpecificClass();

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

person Llyle    schedule 31.01.2009
comment
Этот аргумент неопровержим. Точка абсолютно понятна. - person Alex J; 31.01.2009
comment
Спасибо - мои мысли недавно витали в этом пространстве - рад, что смог добавить свои два цента на сумму :) - person Llyle; 01.02.2009
comment
Я не вспомнил, что вы можете вернуться к общему определению. Это прекрасно объясняет, почему ему нужно знать все типы, составляющие определение. Этого не происходит в обычном определении класса, поэтому я предположил, что то же самое применимо и к дженерикам. Теперь это имеет смысл. - person Alex J; 01.02.2009

Согласно этой статье о дженериках C #

«Видимость универсального типа - это пересечение универсального типа с видимостью типов параметров. Если видимость всех типов C, T1, T2 и T3 установлена ​​как общедоступная, то видимость C также является общедоступной; но если видимость только одного из этих типов является частной, то видимость C является частной. "

Итак, хотя ваш пример и возможен, он не соответствует определенным правилам.

Более подробный источник см. В разделе 25.5.5 (стр. 399) документа Спецификация C #.

Сконструированный тип C доступен, когда доступны все его компоненты C, T1, ..., TN. Точнее, область доступности для сконструированного типа является пересечением области доступности несвязанного универсального типа и областей доступности аргументов типа.

person Rob Walker    schedule 31.01.2009
comment
Итак, ограничение реализации дженериков? Стоит сообщать о подключении? - person Alex J; 31.01.2009
comment
Не кажется логичным разрешать ссылки на типы, на которые ваша сборка не может ссылаться. - person Llyle; 31.01.2009
comment
Фантастика. Спасибо! Эта цитата о пересечении типов привела меня к источнику моей проблемы! - person kdmurray; 23.08.2010
comment
@vanslly: Почему бы и нет? Это кажется частным случаем ограничения, которое запрещает производным типам быть более доступными, чем их родительские, но я не понимаю, почему возможность определять такие типы бесполезна. Во многих ситуациях происхождение типа должно - с точки зрения внешнего кода - быть деталью реализации. В деревьях отношений, где открытые типы W и X должны быть производными от открытого типа Z, иногда полезно иметь промежуточный тип Y, для которого внешний код должен иметь какое-либо законное использование. В таких случаях, что дает требование, чтобы Y был публичным? - person supercat; 25.06.2015
comment
@supercat Это не деталь реализации, общее определение является частью определения типа. Когда я слышу детали реализации, я думаю о композиции и инкапсуляции. Я понимаю, что могут быть обстоятельства, когда общий тип аргумента не отображается в контракте типа (виртуальный метод, свойство и т. Д.), Однако мне это кажется особым случаем. - person Llyle; 09.07.2015

Если бы это было разрешено, вы могли бы передать MyClass из сборки, и внезапно другая сборка получила доступ к чему-то, чего не должна - MyInternalConcrete!

person Ray Hidayat    schedule 31.01.2009