У меня есть класс Foo
и статический класс FooFactory
, который используется для создания экземпляров производных классов Foo
и Foo
с помощью следующего API:
public static class FooFactory {
public static T Create<T>() where T : Foo, new() {
...
return new T();
}
}
Специализация new()
параметра типа T
требует, чтобы Foo
имел конструктор по умолчанию public
. Другими словами, ничто не мешает пользователю Foo
инициализировать класс напрямую через конструктор.
Однако
FooFactory
предназначен для отслеживания всех экземпляровFoo
, поэтому я хочу заставить пользователя создавать все производные экземплярыFoo
иFoo
через файлFooFactory
.
Самое близкое, что мне удалось предотвратить прямую инициализацию конструктора, — это украсить конструктор по умолчанию Foo
атрибутом [Obsolete("message", error: true)]
:
public class Foo {
[Obsolete("Fail", true)]
public Foo() { ... }
}
С этим украшением код не компилируется, когда я напрямую вызываю конструктор по умолчанию Foo
, тогда как инициализация через FooFactory.Create<Foo>()
работает.
Но с этим украшением [Obsolete]
у меня все еще есть проблемы с производными классами. Следующее даже не скомпилируется из-за настройки error: true
для конструктора по умолчанию Foo
:
public class Bar : Foo { ... }
public class Baz : Foo { public Baz() : base() { ... } }
Я могу объявить перегрузку конструктора protected
Foo
, которую вызывают производные классы вместо конструктора по умолчанию, но тогда у меня не будет возможности программно предотвратить прямую инициализацию производных классов.
Если я установлю error
в ObsoleteAttribute
на false
, вместо этого я получу предупреждения о компиляции, но я хотел бы иметь более сильное разочарование, чем предупреждения...
Можно ли программно предотвратить прямой вызов конструктора по умолчанию как для Foo
, так и для производных классов, когда конструкторы по умолчанию должны быть объявлены public
?
internal
библиотекой и удалите ограничениеnew()
. Теперь это означало, что вы несете ответственность за создание всех типов Foo. - person Nkosi   schedule 04.09.2017new()
работает только в том случае, если конструктор объявленpublic
. - person Anders Gustafsson   schedule 04.09.2017Foo
выполнять отслеживание экземпляров, вызывая статическое поле иthis.GetType()
. - person Jeroen Mostert   schedule 04.09.2017Baz
, который может быть сконструирован напрямую — автор может всегда предоставить публичный конструктор для пользователей, который обходит любые ваши требования, независимо от того, как был настроенFoo
. Если вы предполагаете, что авторы всегда находятся в сговоре и могут предположить, что они будут себя вести, моим вторым предложением было бы отказаться от ограниченияnew()
и передать вызов конструктора какFunc<T>
внутреннему методу. Это обеспечивает безопасность типов, но обременительно для авторов. - person Jeroen Mostert   schedule 04.09.2017new()
заключается в том, что она переносима на разные платформы. Отражение, в частности против методов, отличных отpublic
, не всегда хорошо работает на всех платформах. - person Anders Gustafsson   schedule 04.09.2017Foo
? Если это не требование, то решить вашу проблему намного проще. - person Eric Lippert   schedule 04.09.2017