C# 是否存在将我的泛型方法限制为数字类型的约束?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/32664/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is there a constraint that restricts my generic method to numeric types?
提问by Corin Blaikie
Can anyone tell me if there is a way with generics to limit a generic type argument T
to only:
谁能告诉我泛型是否有办法将泛型类型参数限制为T
:
Int16
Int32
Int64
UInt16
UInt32
UInt64
Int16
Int32
Int64
UInt16
UInt32
UInt64
I'm aware of the where
keyword, but can't find an interface for onlythese types,
我知道where
关键字,但找不到仅适用于这些类型的接口,
Something like:
就像是:
static bool IntegerFunction<T>(T value) where T : INumeric
采纳答案by Konrad Rudolph
C# does not support this. Hejlsberg has described the reasons for not implementing the feature in an interview with Bruce Eckel:
C# 不支持这个。Hejlsberg在接受 Bruce Eckel 采访时描述了未实现该功能的原因:
And it's not clear that the added complexity is worth the small yield that you get. If something you want to do is not directly supported in the constraint system, you can do it with a factory pattern. You could have a
Matrix<T>
, for example, and in thatMatrix
you would like to define a dot product method. That of course that means you ultimately need to understand how to multiply twoT
s, but you can't say that as a constraint, at least not ifT
isint
,double
, orfloat
. But what you could do is have yourMatrix
take as an argument aCalculator<T>
, and inCalculator<T>
, have a method calledmultiply
. You go implement that and you pass it to theMatrix
.
并且不清楚增加的复杂性是否值得您获得的小收益。如果约束系统不直接支持你想做的事情,你可以用工厂模式来做。
Matrix<T>
例如,您可以有一个,并且Matrix
您想定义一个点积方法。那当然,这意味着你最终需要了解如何乘两个T
你S,但也不能说,作为一个约束,至少不是如果T
是int
,double
或float
。但是你可以做的是将你的Matrix
作为参数 aCalculator<T>
和 inCalculator<T>
,有一个名为multiply
. 你去实现它,然后将它传递给Matrix
.
However, this leads to fairly convoluted code, where the user has to supply their own Calculator<T>
implementation, for each T
that they want to use. As long as it doesn't have to be extensible, i.e. if you just want to support a fixed number of types, such as int
and double
, you can get away with a relatively simple interface:
然而,这会导致相当复杂的代码,用户必须为他们想要使用的Calculator<T>
每个代码提供他们自己的实现T
。只要它不必是可扩展的,即如果您只想支持固定数量的类型,例如int
and double
,您可以使用相对简单的接口:
var mat = new Matrix<int>(w, h);
(Minimal implementation in a GitHub Gist.)
However, as soon as you want the user to be able to supply their own, custom types, you need to open up this implementation so that the user can supply their own Calculator
instances. For instance, to instantiate a matrix that uses a custom decimal floating point implementation, DFP
, you'd have to write this code:
但是,一旦您希望用户能够提供他们自己的自定义类型,您就需要打开此实现,以便用户可以提供他们自己的Calculator
实例。例如,要实例化一个使用自定义十进制浮点实现的矩阵DFP
,您必须编写以下代码:
var mat = new Matrix<DFP>(DfpCalculator.Instance, w, h);
… and implement all the members for DfpCalculator : ICalculator<DFP>
.
... 并为 实现所有成员DfpCalculator : ICalculator<DFP>
。
An alternative, which unfortunately shares the same limitations, is to work with policy classes, as discussed in Sergey Shandar's answer.
不幸的是,另一种方法具有相同的局限性,即使用策略类,如 Sergey Shandar 的回答中所述。
回答by Keith
There's no constraint for this. It's a real issue for anyone wanting to use generics for numeric calculations.
对此没有任何限制。对于任何想要使用泛型进行数值计算的人来说,这是一个真正的问题。
I'd go further and say we need
我会更进一步说我们需要
static bool GenericFunction<T>(T value)
where T : operators( +, -, /, * )
Or even
甚至
static bool GenericFunction<T>(T value)
where T : Add, Subtract
Unfortunately you only have interfaces, base classes and the keywords struct
(must be value-type), class
(must be reference type) and new()
(must have default constructor)
不幸的是,您只有接口、基类和关键字struct
(必须是值类型)、class
(必须是引用类型)和new()
(必须有默认构造函数)
You could wrap the number in something else (similar to INullable<T>
) like here on codeproject.
您可以将数字包装在其他内容中(类似于INullable<T>
),例如codeproject 上的此处。
You could apply the restriction at runtime (by reflecting for the operators or checking for types) but that does lose the advantage of having the generic in the first place.
您可以在运行时应用限制(通过反映运算符或检查类型),但这确实失去了首先拥有泛型的优势。
回答by Haacked
Probably the closest you can do is
可能你能做的最接近的是
static bool IntegerFunction<T>(T value) where T: struct
Not sure if you could do the following
不确定您是否可以执行以下操作
static bool IntegerFunction<T>(T value) where T: struct, IComparable
, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
For something so specific, why not just have overloads for each type, the list is so short and it would possibly have less memory footprint.
对于如此具体的事情,为什么不只为每种类型设置重载,列表很短,而且可能会占用更少的内存。
回答by Martin Marconcini
I was wondering the same as samjudson, why only to integers? and if that is the case, you might want to create a helper class or something like that to hold all the types you want.
我想知道和 samjudson 一样,为什么只对整数?如果是这种情况,您可能想要创建一个辅助类或类似的东西来保存您想要的所有类型。
If all you want are integers, don't use a generic, that is not generic; or better yet, reject any other type by checking its type.
如果你想要的只是整数,不要使用泛型,那不是泛型;或者更好的是,通过检查其类型来拒绝任何其他类型。
回答by ljs
Unfortunately you are only able to specify struct in the where clause in this instance. It does seem strange you can't specify Int16, Int32, etc. specifically but I'm sure there's some deep implementation reason underlying the decision to not permit value types in a where clause.
不幸的是,在此实例中,您只能在 where 子句中指定 struct 。您不能具体指定 Int16、Int32 等,这似乎很奇怪,但我确信在 where 子句中不允许值类型的决定背后有一些深层的实现原因。
I guess the only solution is to do a runtime check which unfortunately prevents the problem being picked up at compile time. That'd go something like:-
我想唯一的解决方案是进行运行时检查,不幸的是,这会阻止在编译时发现问题。那会是这样的:-
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...
}
Which is a little bit ugly I know, but at least provides the required constraints.
我知道这有点难看,但至少提供了所需的约束。
I'd also look into possible performance implications for this implementation, perhaps there's a faster way out there.
我还会研究此实现可能对性能的影响,也许有更快的方法。
回答by Tom Welch
I think you are misunderstanding generics. If the operation you are trying to perform is only good for specific data types then you are not doing something "generic".
我认为你误解了泛型。如果您尝试执行的操作仅适用于特定数据类型,那么您就没有做一些“通用”的事情。
Also, since you are only wanting to allow the function to work on int data types then you shouldn't need a separate function for each specific size. Simply taking a parameter in the largest specific type will allow the program to automatically upcast the smaller data types to it. (i.e. passing an Int16 will auto-convert to Int64 when calling).
此外,由于您只想允许函数处理 int 数据类型,因此您不需要为每个特定大小使用单独的函数。简单地采用最大的特定类型的参数将允许程序自动向上转换较小的数据类型。(即传递 Int16 将在调用时自动转换为 Int64)。
If you are performing different operations based on the actual size of int being passed into the function then I would think you should seriously reconsider even trying to do what you are doing. If you have to fool the language you should think a bit more about what you are trying to accomplish rather than how to do what you want.
如果您根据传递给函数的 int 的实际大小执行不同的操作,那么我认为您甚至应该认真地重新考虑尝试做您正在做的事情。如果你不得不愚弄语言,你应该更多地考虑你想要完成的事情,而不是如何做你想做的事。
Failing all else, a parameter of type Object could be used and then you will have to check the type of the parameter and take appropriate action or throw an exception.
否则,可以使用 Object 类型的参数,然后您必须检查参数的类型并采取适当的操作或引发异常。
回答by dbkk
What is the point of the exercise?
练习的重点是什么?
As people pointed out already, you could have a non-generic function taking the largest item, and compiler will automatically convert up smaller ints for you.
正如人们已经指出的那样,您可以使用非泛型函数获取最大的项目,编译器会自动为您转换较小的整数。
static bool IntegerFunction(Int64 value) { }
If your function is on performance-critical path (very unlikely, IMO), you could provide overloads for all needed functions.
如果您的功能在性能关键路径上(非常不可能,IMO),您可以为所有需要的功能提供重载。
static bool IntegerFunction(Int64 value) { }
...
static bool IntegerFunction(Int16 value) { }
回答by Marc Gravell
This question is a bit of a FAQ one, so I'm posting this as wiki (since I've posted similar before, but this is an older one); anyway...
这个问题有点像常见问题解答,所以我将此作为 wiki 发布(因为我之前发布过类似的内容,但这是一个较旧的);反正...
What version of .NET are you using? If you are using .NET 3.5, then I have a generic operators implementationin MiscUtil(free etc).
您使用的是什么版本的 .NET?如果您使用的是 .NET 3.5,那么我在MiscUtil(免费等)中有一个通用的运算符实现。
This has methods like T Add<T>(T x, T y)
, and other variants for arithmetic on different types (like DateTime + TimeSpan
).
这有像T Add<T>(T x, T y)
和其他不同类型的算术变体(如DateTime + TimeSpan
)。
Additionally, this works for all the inbuilt, lifted and bespoke operators, and caches the delegate for performance.
此外,这适用于所有内置、提升和定制的运算符,并缓存委托以提高性能。
Some additional background on why this is tricky is here.
关于为什么这很棘手的一些额外背景在这里。
You may also want to know that dynamic
(4.0) sort-of solves this issue indirectly too - i.e.
您可能还想知道dynamic
(4.0) 也间接地解决了这个问题 - 即
dynamic x = ..., y = ...
dynamic result = x + y; // does what you expect
回答by Marc Roussel
I would use a generic one which you could handle externaly...
我会使用一个通用的,你可以处理外部...
/// <summary>
/// Generic object copy of the same type
/// </summary>
/// <typeparam name="T">The type of object to copy</typeparam>
/// <param name="ObjectSource">The source object to copy</param>
public T CopyObject<T>(T ObjectSource)
{
T NewObject = System.Activator.CreateInstance<T>();
foreach (PropertyInfo p in ObjectSource.GetType().GetProperties())
NewObject.GetType().GetProperty(p.Name).SetValue(NewObject, p.GetValue(ObjectSource, null), null);
return NewObject;
}
回答by pomeroy
This limitation affected me when I tried to overload operators for generic types; since there was no "INumeric" constraint, and for a bevy of other reasons the good people on stackoverflow are happy to provide, operations cannot be defined on generic types.
当我尝试为泛型类型重载运算符时,这个限制影响了我;由于没有“INumeric”约束,并且由于许多其他原因,stackoverflow 上的好人很乐意提供,因此无法在泛型类型上定义操作。
I wanted something like
我想要类似的东西
public struct Foo<T>
{
public T Value{ get; private set; }
public static Foo<T> operator +(Foo<T> LHS, Foo<T> RHS)
{
return new Foo<T> { Value = LHS.Value + RHS.Value; };
}
}
I have worked around this issue using .net4 dynamic runtime typing.
我已经使用 .net4 动态运行时类型解决了这个问题。
public struct Foo<T>
{
public T Value { get; private set; }
public static Foo<T> operator +(Foo<T> LHS, Foo<T> RHS)
{
return new Foo<T> { Value = LHS.Value + (dynamic)RHS.Value };
}
}
The two things about using dynamic
are
关于使用的两件事dynamic
是
- Performance. All value types get boxed.
- Runtime errors. You "beat" the compiler, but lose type safety. If the generic type doesn't have the operator defined, an exception will be thrown during execution.
- 表现。所有值类型都被装箱。
- 运行时错误。您“击败”了编译器,但失去了类型安全性。如果泛型类型没有定义运算符,则在执行过程中会抛出异常。