Подтвердить что ты не робот

Поддерживается ли общий конструктор в не-универсальном классе?

Не поддерживается ли, поддерживается ли это, но я должен сделать некоторые трюки?

Пример:

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

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

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

4b9b3361

Ответ 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();
}

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