如何将通用类型限制为基本类型?

98

我有一个带有泛型类型的方法:

T GetValue<T>();

我希望将T限制为原始类型,例如int、string和float,但不包括类类型。我知道可以像这样为类类型定义泛型:

C GetObject<C>() where C: class;

我不确定原始类型是否可以实现,如果可以的话,该如何实现。


我认为@David.Chu.ca所说的“原始类型”是指未经管理的类型,即intfloat等,而不是Int32Int64Single等。虽然class表示引用类型,“但不是类类型”表明了未经管理和经管理的基元之间的区别。 - mireazma
人们经常遇到这种情况。值得注意的是,许多得到赞同的答案建议在T上添加描述接口行为的约束条件。请注意这些答案,因为它们是很好的指导。在90%的情况下,当您认为需要将T限制为原始类型时,您可能真正遇到的是希望对T执行某些操作的愿望。例如,一些操作(如比较T == T)具有非明显的解决方案。T == T使用以下方式执行:EqualityComparer<T>.Default.Equals(a, b)。尽管该行代码难看,但它编译成非常快速的代码。 - JamesHoux
7个回答

58
你可以使用这个来限制值类型:
where C: struct

你也提到了字符串。不幸的是,由于它们不是值类型,所以字符串是不允许的。


6
但对于可为空的字符串则不适用。 - David.Chu.ca
6
当然,它可以通过任何用户定义的结构类型,而不仅仅是原始类型。实际上,除了为所有内置的原始类型定义重载之外,我不确定是否有其他方法。 - Matt Hamilton
2
这个问题是关于原始类型而不是值类型的。正如你在回答中提到的,字符串原始类型不是值类型,因此在这方面它不起作用 - 同时它将允许你传递随机结构体(正如@MattHamilton指出的那样),这些结构体不是原始类型。 - BrainSlugs83
1
如果我有一个方法,它执行n1 + n2,其中n1和n2都是类型为C的变量,那么它会抛出错误:运算符'+'不能应用于类型为'C'和'C'的操作数 - Sisir

31

实际上这个可以在一定程度上完成任务:

public T Object<T>() where T :
   struct, IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>

要限制为数字类型,您可以使用以下示例为ValueType类定义的一些有用提示。


1
注意:如果您像我一样将此用作数据库中的ID字段,请注意,Guid结构不支持“IConvertible”接口,您可能需要将其删除。 - Hassan Faghihi

18
这是你要找的内容:
T GetObject<T>() where T : struct;

真糟糕!我讨厌别人比我先一步。干得好,BFree! - Joshua Belden
4
字符串是原始类型但可为空的类型,怎么样? - David.Chu.ca
4
@David:string 不是原始类型,它是一种引用类型,在某些情况下被视为原始类型。 - Samuel
但是没有办法将动态检索到的对象转换为符合那个“struct”的要求,对吧? - Nyerguds
@Nyerguds 这篇帖子可能会有所帮助,https://dev59.com/bYzda4cB1Zd3GeqPs_Ct。 - Joshua Belden
4
String是一种原始类型,但不是结构体/值类型。 - BrainSlugs83

11

没有一种通用约束能够干净地匹配这些东西。你实际想做什么?例如,您可以通过运行时检查进行黑客攻击,例如静态 ctor(对于泛型类型-对于泛型方法不太容易)...

然而; 大多数情况下我看到的是因为人们想要其中之一:

  • 能够检查项目是否相等:在这种情况下,请使用 EqualityComparer<T>.Default
  • 能够比较/排序项目:在这种情况下,请使用 Comparer<T>.Default
  • 能够执行算术操作:在这种情况下,请使用MiscUtil支持的通用运算符

@MehdiDehghani https://www.nuget.org/packages/JonSkeet.MiscUtil/,https://jonskeet.uk/csharp/miscutil/ - Marc Gravell

9

你在方法中实际上想要做什么?也许你需要使用C来实现IComparable或其他接口。如果是这样,你需要像下面这样:

T GetObject<T> where T: IComparable

4
我需要翻译的内容如下:

我有同样的需求,希望创建一个方法,该方法应检索一个列表,其中T应为原始类型,如int、double、decimal等。

根据Microsoft文档: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/unmanaged-types

看起来正确的方法是使用

where T : unmanaged

引用:

如果类型是以下任何类型,则该类型是未管理类型:

sbyte、byte、short、ushort、int、uint、long、ulong、char、float、double、decimal 或 bool 任何枚举类型 任何指针类型 仅包含未管理类型字段的任何用户定义的结构类型,并且在 C# 7.3 及更早版本中,它不是构造类型(即至少包括一个类型参数的类型)

另外重要引用:

从 C# 7.3 开始,您可以使用未管理约束来指定类型参数是非指针、非可空未管理类型。

从 C# 8.0 开始,仅包含未管理类型字段的构造结构类型也是未管理类型...


1
为了避免 C# 8.0 将结构体视为“未管理”的扩展,您可能仍然可以通过之前的 unmanaged, IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T> 等方式进行限制。我希望原始类型实际上能够实现一些允许唯一识别它们的东西(例如 IPrimitive,其他类型无法实现)... - Ray
1
unmanaged 不包含 string 类型,只需要为 string 添加一个重载。 - Mr. Squirrel.Downy
1
unmanaged 不包含 string 类型,只需要为 string 添加一个重载。 - undefined

0
如果需要使用像sizeof操作符这样的管理类型保留语言特性,可以使用“非托管”类型。
where C: unmanaged

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