Cor*_*kie 350 c# generics constraints
任何人都可以告诉我是否有一种方法可以使用泛型来限制泛型类型参数T:
Int16Int32Int64UInt16UInt32UInt64我知道的where关键字,但无法找到一个接口只有这些类型,
就像是:
static bool IntegerFunction<T>(T value) where T : INumeric
Run Code Online (Sandbox Code Playgroud)
Kon*_*lph 132
Hejlsberg 在接受 Bruce Eckel 采访时描述了不实现该功能的原因.
不过,我不得不承认,我不知道他认为他提议的解决方法是如何工作的.他的建议是将算术运算推迟到其他一些通用类(阅读采访!).这有什么用?恕我直言,并不多.
Jer*_*vel 95
考虑到这个问题的普及以及这种功能背后的兴趣,我很惊讶地发现还没有涉及T4的答案.
在这个示例代码中,我将演示一个非常简单的示例,说明如何使用强大的模板引擎来完成编译器在后台使用泛型执行的操作.
您可以简单地为您喜欢的每种类型生成所需的函数,并相应地使用它(在编译时!),而不是通过箍和牺牲编译时的确定性.
为此:
<#@ template language="C#" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Core" #>
<# Type[] types = new[] {
typeof(Int16), typeof(Int32), typeof(Int64),
typeof(UInt16), typeof(UInt32), typeof(UInt64)
};
#>
using System;
public static class MaxMath {
<# foreach (var type in types) {
#>
public static <#= type.Name #> Max (<#= type.Name #> val1, <#= type.Name #> val2) {
return val1 > val2 ? val1 : val2;
}
<#
} #>
}
Run Code Online (Sandbox Code Playgroud)
而已.你现在完成了.
保存此文件将自动将其编译为此源文件:
using System;
public static class MaxMath {
public static Int16 Max (Int16 val1, Int16 val2) {
return val1 > val2 ? val1 : val2;
}
public static Int32 Max (Int32 val1, Int32 val2) {
return val1 > val2 ? val1 : val2;
}
public static Int64 Max (Int64 val1, Int64 val2) {
return val1 > val2 ? val1 : val2;
}
public static UInt16 Max (UInt16 val1, UInt16 val2) {
return val1 > val2 ? val1 : val2;
}
public static UInt32 Max (UInt32 val1, UInt32 val2) {
return val1 > val2 ? val1 : val2;
}
public static UInt64 Max (UInt64 val1, UInt64 val2) {
return val1 > val2 ? val1 : val2;
}
}
Run Code Online (Sandbox Code Playgroud)
在您的main方法中,您可以验证您是否具有编译时确定性:
namespace TTTTTest
{
class Program
{
static void Main(string[] args)
{
long val1 = 5L;
long val2 = 10L;
Console.WriteLine(MaxMath.Max(val1, val2));
Console.Read();
}
}
}
Run Code Online (Sandbox Code Playgroud)

我会提前一句话:不,这不违反DRY原则.DRY原则是为了防止人们在多个地方复制代码,导致应用程序难以维护.
这里的情况并非如此:如果您想要进行更改,那么您只需更改模板(所有代的单一来源!)即可完成.
要将它与您自己的自定义定义一起使用,请向生成的代码添加名称空间声明(确保它与您定义自己的实现的声明相同)并将该类标记为partial.然后,将这些行添加到模板文件中,以便它包含在最终编译中:
<#@ import namespace="TheNameSpaceYouWillUse" #>
<#@ assembly name="$(TargetPath)" #>
Run Code Online (Sandbox Code Playgroud)
老实说:这很酷.
免责声明:此示例受到了来自Kevin Hazzard和Jason Bock,Manning Publications的.NET中元编程的严重影响.
Kei*_*ith 84
对此没有任何限制.对于想要使用泛型进行数值计算的人来说,这是一个真正的问题.
我会更进一步说我们需要
static bool GenericFunction<T>(T value)
where T : operators( +, -, /, * )
Run Code Online (Sandbox Code Playgroud)
甚至
static bool GenericFunction<T>(T value)
where T : Add, Subtract
Run Code Online (Sandbox Code Playgroud)
不幸的是,你只有接口,基类和关键字struct(必须是值类型),class(必须是引用类型)和new()(必须有默认构造函数)
您可以将数字包装在codeproject上的其他内容(类似于INullable<T>)中.
您可以在运行时应用限制(通过反映运算符或检查类型),但这确实失去了首先使用泛型的优势.
Ser*_*dar 57
使用策略的解决方法:
interface INumericPolicy<T>
{
T Zero();
T Add(T a, T b);
// add more functions here, such as multiplication etc.
}
struct NumericPolicies:
INumericPolicy<int>,
INumericPolicy<long>
// add more INumericPolicy<> for different numeric types.
{
int INumericPolicy<int>.Zero() { return 0; }
long INumericPolicy<long>.Zero() { return 0; }
int INumericPolicy<int>.Add(int a, int b) { return a + b; }
long INumericPolicy<long>.Add(long a, long b) { return a + b; }
// implement all functions from INumericPolicy<> interfaces.
public static NumericPolicies Instance = new NumericPolicies();
}
Run Code Online (Sandbox Code Playgroud)
算法:
static class Algorithms
{
public static T Sum<P, T>(this P p, params T[] a)
where P: INumericPolicy<T>
{
var r = p.Zero();
foreach(var i in a)
{
r = p.Add(r, i);
}
return r;
}
}
Run Code Online (Sandbox Code Playgroud)
用法:
int i = NumericPolicies.Instance.Sum(1, 2, 3, 4, 5);
long l = NumericPolicies.Instance.Sum(1L, 2, 3, 4, 5);
NumericPolicies.Instance.Sum("www", "") // compile-time error.
Run Code Online (Sandbox Code Playgroud)
该解决方案是编译时安全的.CityLizard Framework提供.NET 4.0的编译版本.该文件是lib/NETFramework4.0/CityLizard.Policy.dll.
它也可以在Nuget中找到:https://www.nuget.org/packages/CityLizard/ .请参见CityLizard.Policy.I结构.
Kap*_*nir 18
从 C# 7.3 开始,您可以使用更接近的近似值-非托管约束来指定类型参数是非指针、不可为 null 的非托管类型。
class SomeGeneric<T> where T : unmanaged
{
//...
}
Run Code Online (Sandbox Code Playgroud)
非托管约束意味着 struct 约束,并且不能与 struct 或 new() 约束结合使用。
如果类型是以下任一类型,则该类型为非托管类型:
要进一步限制并消除未实现 IComparable 的指针和用户定义类型,请添加IComparable(但 enum 仍然是从 IComparable 派生的,因此通过添加 IEquatable < T > 来限制 enum,您可以根据自己的情况进一步添加额外的接口。非托管允许使此列表更短):
class SomeGeneric<T> where T : unmanaged, IComparable, IEquatable<T>
{
//...
}
Run Code Online (Sandbox Code Playgroud)
但这并不能阻止 DateTime 实例化。
Dan*_*Dan 16
.Net 7 中存在此限制。
从 .NET 7 开始,您可以使用INumber和等接口IFloatingPoint来创建如下程序:
using System.Numerics;
Console.WriteLine(Sum(1, 2, 3, 4, 5));
Console.WriteLine(Sum(10.541, 2.645));
Console.WriteLine(Sum(1.55f, 5, 9.41f, 7));
static T Sum<T>(params T[] numbers) where T : INumber<T>
{
T result = T.Zero;
foreach (T item in numbers)
{
result += item;
}
return result;
}
Run Code Online (Sandbox Code Playgroud)
INumber位于System.Numerics命名空间中。
还有诸如IAdditionOperators和之类的接口IComparisonOperators,因此您可以一般地使用特定的运算符。
Mar*_*ell 15
这个问题有点像常见问题解答之一,所以我把它作为wiki发布(因为我以前发过类似的,但这是一个较旧的); 无论如何...
您使用的是什么版本的.NET?如果您使用的是.NET 3.5,那么我在MiscUtil中有一个通用运算符实现(免费等).
这有类似的方法T Add<T>(T x, T y),以及不同类型的算术的其他变体(如DateTime + TimeSpan).
此外,这适用于所有内置,提升和定制的操作员,并缓存代表的性能.
关于为什么这很棘手的一些额外背景在这里.
您可能还想知道dynamic(4.0)排序也间接地解决了这个问题 - 即
dynamic x = ..., y = ...
dynamic result = x + y; // does what you expect
Run Code Online (Sandbox Code Playgroud)
ljs*_*ljs 14
不幸的是,您只能在此实例的where子句中指定struct.看起来很奇怪你不能具体指定Int16,Int32等,但我确信在where子句中不允许值类型的决定存在一些深层实现原因.
我想唯一的解决方案是进行运行时检查,不幸的是,这会阻止在编译时拾取问题.那就像: -
static bool IntegerFunction<T>(T value) where T : struct {
if (typeof(T) != typeof(Int16) &&
typeof(T) != typeof(Int32) &&
typeof(T) != typeof(Int64) &&
typeof(T) != typeof(UInt16) &&
typeof(T) != typeof(UInt32) &&
typeof(T) != typeof(UInt64)) {
throw new ArgumentException(
string.Format("Type '{0}' is not valid.", typeof(T).ToString()));
}
// Rest of code...
}
Run Code Online (Sandbox Code Playgroud)
我知道这有点难看,但至少提供了所需的约束.
我还会研究这种实现可能带来的性能影响,也许还有更快的方法.
Haa*_*ked 13
可能你最接近的是
static bool IntegerFunction<T>(T value) where T: struct
Run Code Online (Sandbox Code Playgroud)
不确定您是否可以执行以下操作
static bool IntegerFunction<T>(T value) where T: struct, IComparable
, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
Run Code Online (Sandbox Code Playgroud)
对于某些特定的东西,为什么不只是为每种类型都有重载,列表是如此之短,它可能会有更少的内存占用.
主题是旧的,但对于未来的读者:
此功能与Discriminated Unions目前尚未在 C# 中实现的功能密切相关。我在这里发现了它的问题:
https://github.com/dotnet/csharplang/issues/113
这个问题仍然是开放的,并且已经计划了这个功能 C# 10
所以我们还得再等一会儿,但发布后你可以这样做:
static bool IntegerFunction<T>(T value) where T : Int16 | Int32 | Int64 | ...
Run Code Online (Sandbox Code Playgroud)
小智 5
无法将模板限制为类型,但您可以根据类型定义不同的操作。作为通用数字包的一部分,我需要一个通用类来添加两个值。
class Something<TCell>
{
internal static TCell Sum(TCell first, TCell second)
{
if (typeof(TCell) == typeof(int))
return (TCell)((object)(((int)((object)first)) + ((int)((object)second))));
if (typeof(TCell) == typeof(double))
return (TCell)((object)(((double)((object)first)) + ((double)((object)second))));
return second;
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,typeofs 是在编译时计算的,因此编译器将删除 if 语句。编译器还会删除虚假的强制转换。所以有些东西会在编译器中解决
internal static int Sum(int first, int second)
{
return first + second;
}
Run Code Online (Sandbox Code Playgroud)