Вот практический пример того, как вы хотели бы иметь дополнительный тип типа конструктора и обходной путь.
Я собираюсь ввести простую 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>
, сохраняя оба подсчета ссылок объектов, т. Е. Только в том случае, когда оба экземпляра расположены для размещения базового объекта.
Лучше всего, если вы могли бы написать (например, C++, люди могут сделать)
public RefCounted(RefCounted<U> other)
{
...whatever...
}
Но C# не позволяет. Поэтому решение использует некоторую косвенность.
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();
}
Если у вашего класса есть поля, которые имеют общий тип, у вас нет выбора, кроме как поставить все эти типы в класс. Однако, если вы просто хотели скрыть какой-то тип от конструктора, вам нужно будет использовать вышеприведенный трюк - имея скрытый конструктор, чтобы собрать все вместе и определить нормальную общую функцию для вызова этого конструктора.
Вы можете обойти проблему с одинаковым именем, не допуская общий и не общий класс с тем же именем (серьезно, разрешает ли это C#?). Для родового конструктора родового класса я не думаю, что это слишком отвратительно - это просто более высокая степень порядка. –
Одно замечание - конструктор в родовом классе является общим, потому что класс является общим. Однако (принимая ваш ответ) он не может указать ** дополнительные ** общие аргументы. Спасибо за очень хорошие примеры! – greenoldman
@Peter: Нет, проблема с именем samed-name не является проблемой, потому что, хотя вы * можете * перегружать классы по типу arity, нет никакой двусмысленности. Для примера см. «Tuple». –