【问题标题】:Is generic constructor in non-generic class supported?是否支持非泛型类中的泛型构造函数?
【发布时间】:2015-07-30 02:53:39
【问题描述】:

不支持,支持但我必须做一些技巧?

例子:

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

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

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

【问题讨论】:

    标签: c# generics constructor


    【解决方案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>()
    

    真的好丑……

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

    【讨论】:

    • 您可以通过不允许具有相同名称的泛型和非泛型类来解决同名类问题(说真的,C# 允许这样做吗?)。对于泛型类的泛型构造函数,我不认为它太可怕——它只是更高阶的泛型。
    • 注解——泛型类中的构造函数是泛型的,因为类是泛型的。但是(接受您的回答)它不能指定 extra 通用参数。感谢您提供非常好的示例!
    • @Peter:不,同名类问题不是问题,因为虽然你可以按类型“重载”类,但没有歧义。有关示例,请参阅 Tuple
    • @macias:我不认为构造函数是通用的,正是因为它没有引入任何额外的类型参数。你会认为List&lt;T&gt;.IndexOf 是一个泛型方法,仅仅因为它是一个泛型类型吗?
    • Peter -- 实际上类名重载很有用,因为您将所有静态方法都放在非泛型类中。在 C++ 中,我总是有这样奇怪的对 MyClass<...> 和 MyClassFactory。在 C# 中,您可以有多个 Tuple 类,并且调用 Tuple.Create(5,"hello",4.5) 是“干净的”。
    【解决方案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】:

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

      我将为IDisposable介绍一个简单的RefCounted包装器:

      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&lt;Control&gt; 转换为RefCounted&lt;Button&gt;,同时保持两个对象引用计数,即仅当两个实例都被处置以处置底层对象时。

      最好的方法是你会写(就像 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();
      }
      

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-16
        • 1970-01-01
        • 2019-02-20
        • 2014-07-15
        • 2016-09-18
        • 2010-09-07
        相关资源
        最近更新 更多