Is generic constructor in non-generic class supported?

后端 未结 3 1451
执念已碎
执念已碎 2020-12-03 09:27

Is it not supported, is it supported but I have to do some tricks?

Example:

class Foo
{
  public Foo(Func f1,Func

        
相关标签:
3条回答
  • 2020-12-03 10:12

    Here is an practical example about how you would like to have extra constructor type parameter, and the workaround.

    I am going to introduce a simple RefCounted wrapper for 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;
    }
    

    This seems to be fine. But sooner or later you would like to cast a RefCounted<Control> to RefCounted<Button> whilst keep both object reference counting, i.e. only when both instances being disposed to dispose the underlying object.

    The best way is if you could write (like C++ people can do)

    public RefCounted(RefCounted<U> other)
    {
        ...whatever...
    }
    

    But C# does not allow this. So the solution is use some indirection.

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

    If your class have any fields that are of generic type, you have no choice but to put all those types to the class. However, if you just wanted to hide some type from the constructor, you will need to use the above trick - having a hidden constructor to put everything together, and define a normal generic function to call that constructor.

    0 讨论(0)
  • 2020-12-03 10:20

    No, generic constructors aren't supported in either generic or non-generic classes. Likewise generic events, properties and finalizers aren't supported.

    Just occasionally I agree it would be handy - but the syntax would look pretty awful. For example, suppose you had:

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

    What would

    new Foo<string>()
    

    do? Call the generic constructor of the non-generic class, or the normal constructor of the generic class? You'd have to differentiate between them somehow, and it would be messy :(

    Likewise, consider a generic constructor in a generic class:

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

    How would you call the constructor? Hopefully we can all agree that:

    new Foo<string><int>()
    

    is pretty hideous...

    So yes, semantically it would be occasionally useful - but the resulting ugliness counterbalances that, unfortunately.

    0 讨论(0)
  • 2020-12-03 10:21

    Generic constructors are not supported, but you can get around this by simply defining a generic, static method that returns a new Foo:

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

    which is used like this:

    // 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 讨论(0)
提交回复
热议问题