C# 禁止某些类型的泛型类型约束?

C# 禁止某些类型的泛型类型约束?,c#,generics,C#,Generics,我想创建一个通用方法: MvcHtmlString MyMethod(此HtmlHelper html、字符串标题、IEnumerable值) 我的代码中有许多变量,它们是List,IEnumerable,xxxCollection,T[],等等。。。此方法还有一个重载,它将接受不可枚举的值。是否可以禁止类型参数约束中的特定类(例如string) 我已经创建了这样一个重载: MvcHtmlString MyMethod(此HtmlHelper html、字符串标题、对象值) 这种重载适合处理

我想创建一个通用方法:

MvcHtmlString MyMethod(此HtmlHelper html、字符串标题、IEnumerable值)
我的代码中有许多变量,它们是
List
IEnumerable
xxxCollection
T[]
,等等。。。此方法还有一个重载,它将接受不可枚举的值。是否可以禁止类型参数约束中的特定类(例如
string

我已经创建了这样一个重载:

MvcHtmlString MyMethod(此HtmlHelper html、字符串标题、对象值)

这种重载适合处理单个值,但处理值集合需要稍微不同的实现。但是,
string
实现了
IEnumerable
,因此我所有的字符串变量都将被发送到错误的重载,除非我能告诉编译器它们应该被排除。

假设您只想对模型类型使用此扩展,您可以创建一个模型基类,并将
T
限制为实现它的类型

MvcHtmlString MyMethod<T>(this HtmlHelper html, string title, IEnumerable<T> value)
    where T : ModelBase
MvcHtmlString MyMethod(此HtmlHelper html、字符串标题、IEnumerable值)
其中T:ModelBase
否。约束只能强制实施正约束,不能强制实施负约束

运行时类型检查可能是一种方法:

MvcHtmlString MyMethod<T>(this HtmlHelper html, string title, IEnumerable<T> value)
{
    if (typeof(T) == typeof(string))
    {
        throw new IllegalOperationException(...);
    }
}
MvcHtmlString MyMethod(此HtmlHelper html、字符串标题、IEnumerable值)
{
if(typeof(T)=typeof(string))
{
抛出新的非法操作异常(…);
}
}
如果要传递给方法的所有类型都从同一基类继承,或实现同一接口,则可以使用jrummell的单个约束建议来强制继承

尽管它更不雅观,但如果您想支持异构类型,另一种方法是提供足够的重载来处理您的特定用例:

// supports int, float, DateTime, etc.
MvcHtmlString MyMethod(this HtmlHelper html, string title, IEnumerable<int> value)
MvcHtmlString MyMethod(this HtmlHelper html, string title, IEnumerable<float> value)
MvcHtmlString MyMethod(this HtmlHelper html, string title, IEnumerable<DateTime> value)

// supports implementations of MyInterface
MvcHtmlString MyMethod(this HtmlHelper html, string title, IEnumerable<IMyInterface> value)
//支持int、float、DateTime等。
MvcHtmlString MyMethod(此HtmlHelper html、字符串标题、IEnumerable值)
MvcHtmlString MyMethod(此HtmlHelper html、字符串标题、IEnumerable值)
MvcHtmlString MyMethod(此HtmlHelper html、字符串标题、IEnumerable值)
//支持MyInterface的实现
MvcHtmlString MyMethod(此HtmlHelper html、字符串标题、IEnumerable值)
在上面的示例中,您将无法调用
MyMethod
,因为它不满足任何提供的类型约束。请注意,您不能对此使用类型约束,因为它们不是方法签名的一部分。

不幸的是,不是

只能指定以下类型的约束:

where T : struct               // T must be a value type
where T : class                // T must be a reference type
where T : new()                // T must have a parameterless constructor
where T : <base class name>    // T must inherit from <base class>
where T : <interface name>     // T must implement <interface>
where T : U                    // T must inherit from U, where U is another
                               // generic parameter
其中T:struct//T必须是值类型
其中T:class//T必须是引用类型
其中T:new()//T必须具有无参数构造函数
其中T://T必须继承自
其中T://T必须实现
其中T:U//T必须从U继承,其中U是另一个
//通用参数
参考文献:

现在,您可以使用其中一些来限制类型,而不是锁定类型,只指定允许的类型,例如接口实现约束

您可以使用接口或基类约束明确地说“我允许的类型都必须具有以下特征”。我认为这是你最好的选择


您确定泛型是这里的正确工具吗?

考虑到您处理
字符串的意图,您不能使用约束,也不需要约束

如果您现有的方法接受类型
object
,则类型为
string
的参数将导致您建议的新重载,因为
IEnumerable
object
更匹配。然而,您不需要放弃过载的想法,也不需要求助于运行时检查。只需为
字符串
创建额外的重载即可。通过让编译器更容易地处理场景,避开了
对象
IEnumerable
之间的整个决策

给定

void Foo(object argument)
{
    Console.WriteLine("object");
}

void Foo<T>(IEnumerable<T> argument)
{
    Console.WriteLine("enumerable T");
}

void Foo(string argument)
{
    Console.WriteLine("string");
}
产生输出

string
object
enumerable T
然后可以在单个位置进一步强制字符串进入对象重载

void Foo(string argument)
{
    // Console.WriteLine("string");
    Foo((object)argument);
}

如果将
MyMethod
设置为泛型类
Foo
中的非泛型方法,则可以在静态构造函数中放置类型检查,因此每次运行应用程序时只需检查一次约束,而不是每次调用
MyMethod
时检查一次。请确保使用非常具体/详细的异常消息,因为跟踪静态构造函数中抛出的异常可能会遇到其他挑战。我担心的是,我要传递的值很多时候都是字符串,我希望字符串由非IEnumerable重载处理。但是,string实现了
IEnumerable
,因此它将由错误的重载处理,而不是得到像
string value
这样的结果,结果是
s、t、r、I、n、g、v、a、l、u、e
。第二个结果适用于字符串列表,但不适用于字符串本身。@Zaripheth啊,我明白了。在这种情况下,我将检查
typeof(T)==
typeof(char)`并将字符串传递给适当的方法。您可能希望有一个不会混淆编译器的内部方法,比如
internalmymethodsingle(…)
+1@Zarephith,首先使用更具体(非泛型)的重载,所以为您想要排除(或专门处理)的类型实现非泛型版本。你甚至可以用“过时”属性来标记一个你不想要的,以显示警告/错误。我想要的是重载该方法,以便以一种方式处理列表,并以稍微不同的方式处理单个值。我的问题的目的是为了得到答案
void Foo(string argument)
{
    // Console.WriteLine("string");
    Foo((object)argument);
}