generic NOT constraint where T : !IEnumerable

前端 未结 7 1421
余生分开走
余生分开走 2020-12-03 13:31

As per the title, is it possible to declare type-negating constraints in c# 4 ?

相关标签:
7条回答
  • 2020-12-03 13:39

    one use for this would be an option type.

    public class Option<A,B> 
    where A : !B
    where B : !A
    {
        private readonly A a;
        private readonly B b;
    
        private Option(){}
    
        public Option(A a) 
        {
            this.a = a
        }
    
        public Option(B b)  
        {
            this.b = b
        }
    } 
    

    runtime checking would of course work but you wouldn't have the benefit of type checking at compile time.

    0 讨论(0)
  • 2020-12-03 13:41

    No, but it would be possible to check with an "is" and then handle it appropriately...

    0 讨论(0)
  • 2020-12-03 13:52

    As far as I know it is not possible to do that.

    What you can do is some runtime checking:

    public bool MyGenericMethod<T>()
    {
        // if (T is IEnumerable) // don't do this
    
        if (typeof(T).GetInterface("IEnumerable") == null)
            return false;
    
        // ...
    
        return true;
    }
    
    0 讨论(0)
  • 2020-12-03 13:53

    No - there's no such concept either in C# or in the CLR.

    0 讨论(0)
  • 2020-12-03 13:57

    I found my self trying to implement the same case mentioned in the comments:

    void doIt<T>(IEnumerable<T> what) { }
    void doIt<T>(T whats) { }
    

    I excepted the following code to reference the first method:

    doIt(new List<T>());
    

    But it actually references the second one.

    One solution is to cast the argument like this:

    doIt(new List<T>().AsEnumerable<T>());
    

    The cast could be hidden by another overload:

    void doIt<T>(List<T> whats) {
        doIt(whats.AsEnumerable<T>());
    }
    
    0 讨论(0)
  • 2020-12-03 13:58

    As far as I know a Not contraint is not possible. You CAN use base classes and/or Interfaces to constrain a Generic. Faced with a similar problem leading to runtime failures, I implemented an interface on the classes the Generic was to handle:

    public interface IOperations
    {
    
    }
    
    public static T GenericOperationsFactory<T>(ILogger loggerInstance, ref ExecutionContext executionContext) 
            where T: IOperations
    {
        var operationsContext = Factories.OperationsContextFactory(loggerInstance, ref executionContext);
    
        var operations = typeof(T).GetConstructor(new[] { typeof(OperationsContext) }).Invoke(new object[] { operationsContext });
    
        return (T)operations;
    }
    
    public abstract class OperationsBase:IOperations
    {
        protected OperationsContext Context { get; set; }
    
        public OperationsBase(OperationsContext context)
        {
            Context = context;
        }
    ...
    
    public class ListsOperations : OperationsBase
    {
        public ListsOperations(OperationsContext context) :
            base(context)
        {
    
        }
    

    alternatively:

    public static T GenericOperationsFactory<T>(ILogger loggerInstance, ref ExecutionContext executionContext) 
            where T: OperationsBase
    {
        var operationsContext = Factories.OperationsContextFactory(loggerInstance, ref executionContext);
    
        var operations = typeof(T).GetConstructor(new[] { typeof(OperationsContext) }).Invoke(new object[] { operationsContext });
    
        return (T)operations;
    }
    
    public abstract class OperationsBase
    {
        protected OperationsContext Context { get; set; }
    
        public OperationsBase(OperationsContext context)
        {
            Context = context;
        }
    ...
    
    public class ListsOperations : OperationsBase
    {
        public ListsOperations(OperationsContext context) :
            base(context)
        {
    
        }
    
    0 讨论(0)
提交回复
热议问题