是否支持非泛型类中的泛型构造函数?

Is generic constructor in non-generic class supported?

提问人:greenoldman 提问时间:8/31/2010 更新时间:11/8/2022 访问量:51628

问:

它不支持吗,它是否受支持,但我必须做一些技巧?

例:

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

泛型仅在构造函数中使用,没有依赖于它们的字段/属性,我使用它(泛型)来强制执行 f1 和 f2 的类型关联。

备注:我找到了解决方法 - 静态方法Create,但无论如何我很好奇为什么我对直接方法有问题。

C# 泛型 构造函数

评论


答:

93赞 Jon Skeet 8/31/2010 #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>()

真是太狰狞了......

所以是的,从语义上讲,它偶尔会有用——但不幸的是,由此产生的丑陋抵消了这一点。

评论

0赞 Peter Alexander 8/31/2010
您可以通过不允许具有相同名称的泛型和非泛型类来解决同名类问题(说真的,C# 是否允许这样做?对于泛型类的泛型构造函数,我不认为它太可怕——它只是高阶泛型。
3赞 greenoldman 8/31/2010
泛型类中的构造函数是泛型的,因为该类是泛型的。但是(接受您的答案)它不能指定额外的泛型参数。谢谢你提供了很好的例子!
2赞 Jon Skeet 8/31/2010
@Peter:不,同名类问题不是问题,因为尽管您可以按类型“重载”类,但没有歧义。有关示例,请参阅。Tuple
4赞 Jon Skeet 8/31/2010
@macias:我不认为构造函数是泛型的,正是因为它没有引入任何额外的类型参数。你会考虑是一个泛型方法,仅仅因为它是泛型类型吗?List<T>.IndexOf
0赞 greenoldman 8/31/2010
Peter -- 实际上类名重载很有用,因为你把所有静态方法都放在非泛型类中。在C++我总是有这样奇怪的一对 MyClass<......> 和 MyClassFactory。在 C# 中,可以有多个元组类,并且调用 Tuple.Create(5,“hello”,4.5) 是“干净的”。
35赞 Peter Alexander 8/31/2010 #2

不支持泛型构造函数,但您可以通过简单地定义一个返回新方法的泛型方法来解决这个问题:staticFoo

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);
2赞 Earth Engine 7/24/2014 #3

下面是一个实际示例,说明您希望如何拥有额外的构造函数类型参数以及解决方法。

我将介绍一个简单的包装器:RefCountedIDisposable

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;
}

这似乎很好。但迟早你希望将 to 同时保持两个对象引用计数,即仅当两个实例都被处置时才释放基础对象。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();
}

如果您的类具有任何泛型类型的字段,则您别无选择,只能将所有这些类型放入该类中。但是,如果您只想从构造函数中隐藏某种类型,则需要使用上述技巧 - 使用隐藏的构造函数将所有内容放在一起,并定义一个普通的泛型函数来调用该构造函数。