Поддерживается ли универсальный конструктор в неуниверсальном классе? - PullRequest
41 голосов
/ 31 августа 2010

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

Пример:

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

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

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

Ответы [ 3 ]

65 голосов
/ 31 августа 2010

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

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

public class Foo<T> {}

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

Что бы

new Foo<string>()

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

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

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

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

new Foo<string><int>()

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

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

21 голосов
/ 31 августа 2010

Общие конструкторы не поддерживаются, но вы можете обойти это, просто определив общий 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 голосов
/ 24 июля 2014

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...