2010-08-31 3 views
36

Не поддерживается ли, поддерживается ли это, но я должен сделать некоторые трюки?Поддерживается ли общий конструктор в универсальном классе?

Пример:

class Foo 
{ 
    public Foo<T1,T2>(Func<T1,T2> f1,Func<T2,T1> f2) 
    { 
    ... 
    } 
} 

дженериков используются только в конструктор, не существует поле/свойство не зависит от них, я использую его (дженериков) для обеспечения корреляции типа для f1 и f2.

Замечание: Я нашел обходное решение - статический метод Создание, но в любом случае мне любопытно, почему у меня проблема с простым подходом.

ответ

58

Нет, общие конструкторы не поддерживаются ни в родовых, ни в не общих классах. Аналогично, общие события, свойства и финализаторы не поддерживаются.

Просто изредка я согласен, что это будет удобно - но синтаксис будет выглядеть довольно ужасно. Например, предположим, что у вас:

public class Foo<T> {} 

public class Foo 
{ 
    public Foo<T>() {} 
} 

Что бы

new Foo<string>() 

делать? Вызов универсального конструктора не общего класса или обычного конструктора родового класса? Вы должны были бы различать между ними каким-то образом, и было бы грязно :(

Аналогично, рассмотрим общий конструктор в обобщенном классе:

public class Foo<TClass> 
{ 
    public Foo<TConstructor>() {} 
} 

Как бы вы назвали конструктор Надеюсь, мы можем все согласен, что:

new Foo<string><int>() 

довольно отвратительный ...

Так что да, семантически было бы иногда полезно - но в результате уродства противовесов, что, К сожалению.

+0

Вы можете обойти проблему с одинаковым именем, не допуская общий и не общий класс с тем же именем (серьезно, разрешает ли это C#?). Для родового конструктора родового класса я не думаю, что это слишком отвратительно - это просто более высокая степень порядка. –

+2

Одно замечание - конструктор в родовом классе является общим, потому что класс является общим. Однако (принимая ваш ответ) он не может указать ** дополнительные ** общие аргументы. Спасибо за очень хорошие примеры! – greenoldman

+1

@Peter: Нет, проблема с именем samed-name не является проблемой, потому что, хотя вы * можете * перегружать классы по типу arity, нет никакой двусмысленности. Для примера см. «Tuple». –

15

Generic конструкторы не поддерживаются, но вы можете обойти эту проблему, просто определив общий, 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); 
0

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

Я собираюсь ввести простую 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(); 
} 

Если у вашего класса есть поля, которые имеют общий тип, у вас нет выбора, кроме как поставить все эти типы в класс. Однако, если вы просто хотели скрыть какой-то тип от конструктора, вам нужно будет использовать вышеприведенный трюк - имея скрытый конструктор, чтобы собрать все вместе и определить нормальную общую функцию для вызова этого конструктора.

Смежные вопросы