泛型不受约束,其中 T:!IEnumerable。

75
根据标题,是否有可能在C# 4中声明类型否定的约束?
感兴趣的用例是允许以下重载共存。
    void doIt<T>(T what){} 
    
    void doIt<T>(IEnumerable<T> whats){}

目前存在一些不明确的地方,因为第一个方法中的T可能是一个IEnumerable<>(所以我想指定T不应该是IEnumerable)。

6
就算有,你能描述一个使用案例吗? - Mitch Wheat
1
观察到您有这样的要求确实有些奇怪。您只能针对已知属于类族的类型T进行编码。否则,您如何在泛型中编写代码?在这种情况下,要么您不需要泛型,要么您需要重新审查您的用例。 - this. __curious_geek
4
感兴趣的用例是允许以下重载共存:void doIt<T>(T what){}void doIt<T>(IEnumerable<T> whats){} - 目前存在歧义,因为第一个方法中的 T 可能是 IEnumerable<>(所以我想指定 T 不应该是 IEnumerable)。 - Cel
9
请注意,那些带有接受一个T对象和一个T序列的方法的类型通常会为这两个方法取不同的名称。例如,在List<T>中,有Add和AddRange两个方法。这种做法是有原因的,请按照这种模式操作。 - Eric Lippert
12
为什么有人投了关闭票?虽然问题的答案可能是否定的,但这并不意味着这个问题没有价值。请注意,我只进行翻译,不提供任何解释或其他信息。 - phoog
显示剩余4条评论
7个回答

61

不 - C# 或 CLR 中没有这种概念。


1
这个概念将来会被应用到C#和/或CLR吗? - Rand Random
@RandRandom:我没有听说过有任何计划。 - Jon Skeet
4
请在回答中添加最新的 C# 版本对应的信息,以防将来需要更改。 - serge
6
@Serge:我宁愿不这样做。这将适用于站点上几乎每个涉及语言的问题的答案,而且每次有新版本的C#发布时重新审视每个答案是不切实际的。 - Jon Skeet
3
@JonSkeet:无法重新访问旧答案是你回答时应该指定当前版本的一个强有力的理由。如果你说“截至X版本”,即使在Y版本中发生了变化,你的答案在技术上仍然是正确的。更重要的是,十年后阅读此内容的人会注意到旧版本号,并不会将答案视为绝对真实。 - Travis Reed
4
@TravisReed: 老实说,我认为这会给很多答案增加很多噪音。请注意,问题已经指定了C#的版本,而且人们应该查看答案的日期以获取上下文信息。 - Jon Skeet

8

我发现自己正试图实现评论中提到的相同情况:

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

我预期以下代码将引用第一个方法

doIt(new List<T>());

但实际上它引用的是第二个。

一个解决方案是将参数转换为这样的类型

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

这个操作可以被另一个重载函数所隐藏:

void doIt<T>(List<T> whats) {
    doIt(whats.AsEnumerable<T>());
}

5
据我所知,这是不可能实现的。您可以进行一些运行时检查:
public bool MyGenericMethod<T>()
{
    // if (T is IEnumerable) // don't do this

    if (typeof(T).GetInterface("IEnumerable") == null)
        return false;

    // ...

    return true;
}

3
你不能像那样使用 is - 它用于测试一个对象是否与一个类型兼容。 - Jon Skeet
5
你的意思是 if (typeof(T) == typeof(IEnumerable)) {} - kev

4

这个的一个用途可以是选项类型。

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

运行时检查当然可以工作,但您将无法在编译时获得类型检查的好处。


0
不,但可以通过使用“is”进行检查,然后适当处理它...

-1
据我所知,不可能使用“Not”约束。您可以使用基类和/或接口来约束泛型。面对类似的问题导致运行时失败,我在泛型要处理的类上实现了一个接口:
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)
    {

    }

或者:

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)
    {

    }

-4

你可以使用约束来确保所使用的类型具有一些属性/方法/...你想要使用的

带有类型否定约束的泛型没有任何意义,因为知道某些属性/方法你不想使用是没有目的的。


6
显然,您还没有收到错误消息:“##'<class>' 无法同时实现 '<interface<generictype1,generictype2>>' 和 '<interface<generictype3, generictype4>>',因为它们可能会统一某些类型参数替换。##”... 当您需要指定generictype2不能是generictype4时,确实存在这样的情况。 - Brett Caswell
1
我能够通过使用一系列实现相似接口的抽象类,并继承这些抽象类来解决我的情况。我想这就是Action<T1, T2, T3>等等的实现方式吧。 - Brett Caswell

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接