Поддерживается ли общий конструктор в не-универсальном классе?
Не поддерживается ли, поддерживается ли это, но я должен сделать некоторые трюки?
Пример:
class Foo
{
public Foo<T1,T2>(Func<T1,T2> f1,Func<T2,T1> f2)
{
...
}
}
дженерики используются только в конструкторе, для них нет поля/свойства, я использую его (generics) для принудительной корреляции типов для f1 и f2.
Замечание: я нашел обходное решение - статический метод Create, но в любом случае мне любопытно, почему у меня проблема с простым подходом.
Ответы
Ответ 1
Нет, общие конструкторы не поддерживаются ни в родовых, ни в не общих классах. Аналогично, общие события, свойства и финализаторы не поддерживаются.
Иногда я соглашаюсь, что это будет удобно - но синтаксис будет выглядеть довольно ужасно. Например, предположим, что у вас было:
public class Foo<T> {}
public class Foo
{
public Foo<T>() {}
}
Что будет
new Foo<string>()
делать? Вызов универсального конструктора не общего класса или обычного конструктора родового класса? Вы должны каким-то образом различать их, и это было бы грязно: (
Аналогично рассмотрим общий конструктор в общем классе:
public class Foo<TClass>
{
public Foo<TConstructor>() {}
}
Как вы могли бы назвать конструктор? Надеемся, мы сможем согласиться с тем, что:
new Foo<string><int>()
довольно отвратительно...
Итак, семантически это было бы иногда полезно, но в результате уродство уравновешивает это, к сожалению.
Ответ 2
Общие конструкторы не поддерживаются, но вы можете обойти это, просто определяя общий метод static
, который возвращает новый Foo
:
class Foo
{
public static Foo CreateFromFuncs<T1,T2>(Func<T1,T2> f1,Func<T2,T1> f2)
{
...
}
}
который используется следующим образом:
// create generic dependencies
var func1 = new Func<byte, string>(...);
var func2 = new Func<string, byte>(...);
// create nongeneric Foo from dependencies
Foo myFoo = Foo.CreateFromFuncs<byte, string>(func1, func2);
Ответ 3
Ниже приведен практический пример того, как вы хотели бы иметь дополнительный тип типа конструктора и обходной путь.
Я собираюсь ввести простую обертку RefCounted
для IDisposable
:
public class RefCounted<T> where T : IDisposable
{
public RefCounted(T value)
{
innerValue = value;
refCount = 1;
}
public void AddRef()
{
Interlocked.Increment(ref refCount);
}
public void Dispose()
{
if(InterlockedDecrement(ref refCount)<=0)
innerValue.Dispose();
}
private int refCount;
private readonly innerValue;
}
Кажется, все в порядке. Но рано или поздно вы хотели бы отбросить RefCounted<Control>
до RefCounted<Button>
, пока он не будет содержать подсчет ссылок на объекты, т.е. Только тогда, когда оба экземпляра расположены для размещения базового объекта.
Лучше всего, если вы могли бы написать (например, люди С++)
public RefCounted(RefCounted<U> other)
{
...whatever...
}
Но С# не допускает этого. Поэтому решение использует некоторую косвенность.
private readonly Func<T> valueProvider;
private readonly Action disposer;
private RefCounted(Func<T> value_provider, Action disposer)
{
this.valueProvider = value_provider;
this.disposer = disposer;
}
public RefCounted(T value) : this(() => value, value.Dispose)
{
}
public RefCounted<U> Cast<U>() where U : T
{
AddRef();
return new RefCounted<U>(() => (U)(valueProvider()),this.Dispose);
}
public void Dispose(){
if(InterlockedDecrement(ref refCount)<=0)
disposer();
}
Если у вашего класса есть поля, которые имеют общий тип, у вас нет выбора, кроме как поместить все эти типы в класс. Однако, если вы просто хотели скрыть какой-либо тип от конструктора, вам нужно будет использовать описанный выше трюк - со скрытым конструктором собрать все вместе и определить нормальную общую функцию для вызова этого конструктора.