谁能告诉我泛型是否可以将泛型类型参数限制T
为仅:
Int16
Int32
Int64
UInt16
UInt32
UInt64
我知道这个where
关键字,但是找不到仅适用于这些类型的接口,
就像是:
static bool IntegerFunction<T>(T value) where T : INumeric
谁能告诉我泛型是否可以将泛型类型参数限制T
为仅:
Int16
Int32
Int64
UInt16
UInt32
UInt64
我知道这个where
关键字,但是找不到仅适用于这些类型的接口,
就像是:
static bool IntegerFunction<T>(T value) where T : INumeric
Answers:
C#不支持此功能。Hejlsberg 在接受Bruce Eckel的采访时描述了不实现该功能的原因:
尚不清楚增加的复杂性是否值得您获得少量收益。如果约束系统中不直接支持您要执行的操作,则可以使用工厂模式来执行。
Matrix<T>
例如,您可能有一个,并且Matrix
您想定义一个点积方法。那当然,这意味着你最终需要了解如何乘两个T
你S,但也不能说,作为一个约束,至少不是如果T
是int
,double
或float
。但是,您可以做的是Matrix
将a作为参数Calculator<T>
,并输入Calculator<T>
一个名为的方法multiply
。您去实现它,然后将其传递给Matrix
。
但是,这导致了相当复杂的代码,用户必须为要使用的Calculator<T>
每个代码提供自己的实现T
。只要它不必是可扩展的,即如果您只想支持固定数量的类型(例如int
和)double
,就可以使用一个相对简单的界面:
var mat = new Matrix<int>(w, h);
但是,一旦您希望用户能够提供自己的自定义类型,就需要打开此实现,以便用户可以提供自己的Calculator
实例。例如,要实例化使用自定义十进制浮点实现的矩阵DFP
,您必须编写以下代码:
var mat = new Matrix<DFP>(DfpCalculator.Instance, w, h);
…并实现的所有成员DfpCalculator : ICalculator<DFP>
。
不幸的是,有一个共同的限制,那就是使用策略类,如Sergey Shandar的答案所述。
Operator
/ Operator<T>
; yoda.arachsys.com/csharp/miscutil/usage/genericoperators.html
Operator<T>
(因为访谈是在Expressions
框架存在之前就进行的,尽管可以当然使用Reflection.Emit
))-我会对他的解决方法非常感兴趣。
考虑到这个问题的普遍性以及这种功能背后的兴趣,我很惊讶地看到还没有涉及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;
}
<#
} #>
}
而已。现在完成了。
保存此文件将自动将其编译为该源文件:
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;
}
}
在您的main
方法中,您可以验证您是否具有编译时确定性:
namespace TTTTTest
{
class Program
{
static void Main(string[] args)
{
long val1 = 5L;
long val2 = 10L;
Console.WriteLine(MaxMath.Max(val1, val2));
Console.Read();
}
}
}
我先说一句话:不,这不违反DRY原则。DRY原则是为了防止人们在多个地方复制代码,这将导致应用程序变得难以维护。
这里根本不是这种情况:如果您想进行更改,则只需更改模板(您这一代人的单一来源!)就可以完成。
为了将其与您自己的自定义定义一起使用,请在生成的代码中添加一个名称空间声明(确保它与定义自己的实现的定义相同),并将该类标记为partial
。然后,将这些行添加到模板文件中,以便将其包括在最终的编译中:
<#@ import namespace="TheNameSpaceYouWillUse" #>
<#@ assembly name="$(TargetPath)" #>
坦白说:这很酷。
免责声明:该示例受到Manning Publications的Kevin Hazzard和Jason Bock的.NET元编程的严重影响。
T
从各个IntX
类继承或从各个类继承的某种泛型类型?我喜欢这种解决方案,因为它可以节省时间,但要100%地解决该问题(尽管不如C#支持内置的这种约束那样好),每个生成的方法仍应是通用的,这样他们可以返回从一个IntXX
类继承的类型的对象。
IntXX
类型是结构,这意味着它们首先不支持继承。即使这样做,Liskov替换原理(您可能会从SOLID习惯用法中得知)也适用:如果该方法定义为X
并且Y
是的子代,X
那么根据定义,任何方法Y
都应该能够传递给该方法作为的替代它的基本类型。
对此没有任何限制。对于任何想使用泛型进行数值计算的人来说,这都是一个现实问题。
我走得更远,说我们需要
static bool GenericFunction<T>(T value)
where T : operators( +, -, /, * )
甚至
static bool GenericFunction<T>(T value)
where T : Add, Subtract
不幸的是,您只有接口,基类和关键字struct
(必须是值类型),class
(必须是引用类型)和new()
(必须具有默认构造函数)
您可以将数字包装在codeproject上的其他类似(类似于INullable<T>
)中。
您可以在运行时应用限制(通过反映操作符或检查类型),但这确实失去了首先拥有泛型的优势。
where T : operators( +, -, /, * )
合法的C#吗?对不起,新手问题。
where T : operators( +, -, /, * )
,但不能。
使用策略的解决方法:
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();
}
算法:
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;
}
}
用法:
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.
该解决方案在编译时安全。CityLizard Framework提供了.NET 4.0的编译版本。该文件是lib / NETFramework4.0 / CityLizard.Policy.dll。
在Nuget中也可以使用它:https ://www.nuget.org/packages/CityLizard/ 。请参阅CityLizard.Policy.I结构。
struct
?如果我改用singleton-class并将实例更改为public static NumericPolicies Instance = new NumericPolicies();
然后添加此构造函数,该怎么办private NumericPolicies() { }
?
T Add<T> (T t1, T t2)
,但Sum()
仅在它可以从其参数中检索自己的T类型时才有效。当它嵌入另一个通用函数中时。
这个问题有点像是一个常见问题,所以我将其发布为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
不幸的是,在这种情况下,您只能在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...
}
我知道这有点难看,但至少提供了所需的约束。
我还将研究此实现可能对性能产生的影响,也许有一种更快的方法。
// Rest of code...
如果它取决于约束定义的操作,则可能无法编译。
// Rest of code...
like value + value
或进行任何算术运算时value * value
,就会出现编译错误。
从C#7.3开始,可以使用更接近的近似值 - 非托管约束来指定类型参数是非指针,不可空的非托管类型。
class SomeGeneric<T> where T : unmanaged
{
//...
}
非托管约束暗含结构约束,不能与struct或new()约束结合使用。
如果类型是以下任何类型,则它是非托管类型:
要进一步限制并消除未实现IComparable的指针和用户定义的类型,请添加IComparable(但枚举仍是从IComparable派生的,因此可以通过添加IEquatable <T>来限制枚举,您可以根据具体情况进一步扩展并添加其他接口。不受管理允许将此列表缩短):
class SomeGeneric<T> where T : unmanaged, IComparable, IEquatable<T>
{
//...
}
DateTime
瀑布下的unmanaged, IComparable, IEquatable<T>
约束..
无法将模板限制为类型,但是您可以根据类型定义不同的操作。作为通用数字包的一部分,我需要一个通用类来添加两个值。
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;
}
}
请注意,typeof是在编译时评估的,因此if语句将由编译器删除。编译器还会删除虚假的强制类型转换。因此,某些东西会在编译器中解析为
internal static int Sum(int first, int second)
{
return first + second;
}
我创建了一些库功能来解决这些问题:
代替:
public T DifficultCalculation<T>(T a, T b)
{
T result = a * b + a; // <== WILL NOT COMPILE!
return result;
}
Console.WriteLine(DifficultCalculation(2, 3)); // Should result in 8.
您可以这样写:
public T DifficultCalculation<T>(Number<T> a, Number<T> b)
{
Number<T> result = a * b + a;
return (T)result;
}
Console.WriteLine(DifficultCalculation(2, 3)); // Results in 8.
你可以在这里找到源代码:/codereview/26022/improvement-requested-for-generic-calculator-and-generic-number
我在想和samjudson一样,为什么只求整数?如果是这种情况,您可能想要创建一个帮助器类或类似的东西来保存所需的所有类型。
如果您想要的只是整数,请不要使用泛型,即非泛型;或者更好的是,通过检查其他类型来拒绝其他任何类型。
如果使用的是.NET 4.0及更高版本,则可以仅将dynamic用作方法参数,并在运行时检查传递的动态参数类型为数字/整数类型。
如果通过的类型动态是不是数字/整数类型则抛出异常。
实现该想法的示例短代码如下所示:
using System;
public class InvalidArgumentException : Exception
{
public InvalidArgumentException(string message) : base(message) {}
}
public class InvalidArgumentTypeException : InvalidArgumentException
{
public InvalidArgumentTypeException(string message) : base(message) {}
}
public class ArgumentTypeNotIntegerException : InvalidArgumentTypeException
{
public ArgumentTypeNotIntegerException(string message) : base(message) {}
}
public static class Program
{
private static bool IntegerFunction(dynamic n)
{
if (n.GetType() != typeof(Int16) &&
n.GetType() != typeof(Int32) &&
n.GetType() != typeof(Int64) &&
n.GetType() != typeof(UInt16) &&
n.GetType() != typeof(UInt32) &&
n.GetType() != typeof(UInt64))
throw new ArgumentTypeNotIntegerException("argument type is not integer type");
//code that implements IntegerFunction goes here
}
private static void Main()
{
Console.WriteLine("{0}",IntegerFunction(0)); //Compiles, no run time error and first line of output buffer is either "True" or "False" depends on the code that implements "Program.IntegerFunction" static method.
Console.WriteLine("{0}",IntegerFunction("string")); //Also compiles but it is run time error and exception of type "ArgumentTypeNotIntegerException" is thrown here.
Console.WriteLine("This is the last Console.WriteLine output"); //Never reached and executed due the run time error and the exception thrown on the second line of Program.Main static method.
}
当然,该解决方案只能在运行时有效,而不能在编译时有效。
如果要使解决方案始终在编译时起作用,而从不在运行时起作用,则必须用公共结构/类包装动态程序,该结构/类的重载公共构造函数仅接受所需类型的参数,并为结构/类指定适当的名称。
有道理的是,动态包装始终是类/结构的私有成员,并且是结构/类的唯一成员,并且结构/类的唯一成员的名称是“值”。
如果需要,您还必须为类/结构的私有动态成员定义和实现与所需类型一起使用的公共方法和/或运算符。
同样有意义的是,struct / class具有特殊的/唯一的构造函数,该构造函数接受dynamic作为参数来初始化它唯一的私有动态成员,称为“值”,但是此构造函数的修饰符当然是私有的。
准备好类/结构后,将IntegerFunction的参数类型定义为已定义的类/结构。
实现该想法的示例长代码如下:
using System;
public struct Integer
{
private dynamic value;
private Integer(dynamic n) { this.value = n; }
public Integer(Int16 n) { this.value = n; }
public Integer(Int32 n) { this.value = n; }
public Integer(Int64 n) { this.value = n; }
public Integer(UInt16 n) { this.value = n; }
public Integer(UInt32 n) { this.value = n; }
public Integer(UInt64 n) { this.value = n; }
public Integer(Integer n) { this.value = n.value; }
public static implicit operator Int16(Integer n) { return n.value; }
public static implicit operator Int32(Integer n) { return n.value; }
public static implicit operator Int64(Integer n) { return n.value; }
public static implicit operator UInt16(Integer n) { return n.value; }
public static implicit operator UInt32(Integer n) { return n.value; }
public static implicit operator UInt64(Integer n) { return n.value; }
public static Integer operator +(Integer x, Int16 y) { return new Integer(x.value + y); }
public static Integer operator +(Integer x, Int32 y) { return new Integer(x.value + y); }
public static Integer operator +(Integer x, Int64 y) { return new Integer(x.value + y); }
public static Integer operator +(Integer x, UInt16 y) { return new Integer(x.value + y); }
public static Integer operator +(Integer x, UInt32 y) { return new Integer(x.value + y); }
public static Integer operator +(Integer x, UInt64 y) { return new Integer(x.value + y); }
public static Integer operator -(Integer x, Int16 y) { return new Integer(x.value - y); }
public static Integer operator -(Integer x, Int32 y) { return new Integer(x.value - y); }
public static Integer operator -(Integer x, Int64 y) { return new Integer(x.value - y); }
public static Integer operator -(Integer x, UInt16 y) { return new Integer(x.value - y); }
public static Integer operator -(Integer x, UInt32 y) { return new Integer(x.value - y); }
public static Integer operator -(Integer x, UInt64 y) { return new Integer(x.value - y); }
public static Integer operator *(Integer x, Int16 y) { return new Integer(x.value * y); }
public static Integer operator *(Integer x, Int32 y) { return new Integer(x.value * y); }
public static Integer operator *(Integer x, Int64 y) { return new Integer(x.value * y); }
public static Integer operator *(Integer x, UInt16 y) { return new Integer(x.value * y); }
public static Integer operator *(Integer x, UInt32 y) { return new Integer(x.value * y); }
public static Integer operator *(Integer x, UInt64 y) { return new Integer(x.value * y); }
public static Integer operator /(Integer x, Int16 y) { return new Integer(x.value / y); }
public static Integer operator /(Integer x, Int32 y) { return new Integer(x.value / y); }
public static Integer operator /(Integer x, Int64 y) { return new Integer(x.value / y); }
public static Integer operator /(Integer x, UInt16 y) { return new Integer(x.value / y); }
public static Integer operator /(Integer x, UInt32 y) { return new Integer(x.value / y); }
public static Integer operator /(Integer x, UInt64 y) { return new Integer(x.value / y); }
public static Integer operator %(Integer x, Int16 y) { return new Integer(x.value % y); }
public static Integer operator %(Integer x, Int32 y) { return new Integer(x.value % y); }
public static Integer operator %(Integer x, Int64 y) { return new Integer(x.value % y); }
public static Integer operator %(Integer x, UInt16 y) { return new Integer(x.value % y); }
public static Integer operator %(Integer x, UInt32 y) { return new Integer(x.value % y); }
public static Integer operator %(Integer x, UInt64 y) { return new Integer(x.value % y); }
public static Integer operator +(Integer x, Integer y) { return new Integer(x.value + y.value); }
public static Integer operator -(Integer x, Integer y) { return new Integer(x.value - y.value); }
public static Integer operator *(Integer x, Integer y) { return new Integer(x.value * y.value); }
public static Integer operator /(Integer x, Integer y) { return new Integer(x.value / y.value); }
public static Integer operator %(Integer x, Integer y) { return new Integer(x.value % y.value); }
public static bool operator ==(Integer x, Int16 y) { return x.value == y; }
public static bool operator !=(Integer x, Int16 y) { return x.value != y; }
public static bool operator ==(Integer x, Int32 y) { return x.value == y; }
public static bool operator !=(Integer x, Int32 y) { return x.value != y; }
public static bool operator ==(Integer x, Int64 y) { return x.value == y; }
public static bool operator !=(Integer x, Int64 y) { return x.value != y; }
public static bool operator ==(Integer x, UInt16 y) { return x.value == y; }
public static bool operator !=(Integer x, UInt16 y) { return x.value != y; }
public static bool operator ==(Integer x, UInt32 y) { return x.value == y; }
public static bool operator !=(Integer x, UInt32 y) { return x.value != y; }
public static bool operator ==(Integer x, UInt64 y) { return x.value == y; }
public static bool operator !=(Integer x, UInt64 y) { return x.value != y; }
public static bool operator ==(Integer x, Integer y) { return x.value == y.value; }
public static bool operator !=(Integer x, Integer y) { return x.value != y.value; }
public override bool Equals(object obj) { return this == (Integer)obj; }
public override int GetHashCode() { return this.value.GetHashCode(); }
public override string ToString() { return this.value.ToString(); }
public static bool operator >(Integer x, Int16 y) { return x.value > y; }
public static bool operator <(Integer x, Int16 y) { return x.value < y; }
public static bool operator >(Integer x, Int32 y) { return x.value > y; }
public static bool operator <(Integer x, Int32 y) { return x.value < y; }
public static bool operator >(Integer x, Int64 y) { return x.value > y; }
public static bool operator <(Integer x, Int64 y) { return x.value < y; }
public static bool operator >(Integer x, UInt16 y) { return x.value > y; }
public static bool operator <(Integer x, UInt16 y) { return x.value < y; }
public static bool operator >(Integer x, UInt32 y) { return x.value > y; }
public static bool operator <(Integer x, UInt32 y) { return x.value < y; }
public static bool operator >(Integer x, UInt64 y) { return x.value > y; }
public static bool operator <(Integer x, UInt64 y) { return x.value < y; }
public static bool operator >(Integer x, Integer y) { return x.value > y.value; }
public static bool operator <(Integer x, Integer y) { return x.value < y.value; }
public static bool operator >=(Integer x, Int16 y) { return x.value >= y; }
public static bool operator <=(Integer x, Int16 y) { return x.value <= y; }
public static bool operator >=(Integer x, Int32 y) { return x.value >= y; }
public static bool operator <=(Integer x, Int32 y) { return x.value <= y; }
public static bool operator >=(Integer x, Int64 y) { return x.value >= y; }
public static bool operator <=(Integer x, Int64 y) { return x.value <= y; }
public static bool operator >=(Integer x, UInt16 y) { return x.value >= y; }
public static bool operator <=(Integer x, UInt16 y) { return x.value <= y; }
public static bool operator >=(Integer x, UInt32 y) { return x.value >= y; }
public static bool operator <=(Integer x, UInt32 y) { return x.value <= y; }
public static bool operator >=(Integer x, UInt64 y) { return x.value >= y; }
public static bool operator <=(Integer x, UInt64 y) { return x.value <= y; }
public static bool operator >=(Integer x, Integer y) { return x.value >= y.value; }
public static bool operator <=(Integer x, Integer y) { return x.value <= y.value; }
public static Integer operator +(Int16 x, Integer y) { return new Integer(x + y.value); }
public static Integer operator +(Int32 x, Integer y) { return new Integer(x + y.value); }
public static Integer operator +(Int64 x, Integer y) { return new Integer(x + y.value); }
public static Integer operator +(UInt16 x, Integer y) { return new Integer(x + y.value); }
public static Integer operator +(UInt32 x, Integer y) { return new Integer(x + y.value); }
public static Integer operator +(UInt64 x, Integer y) { return new Integer(x + y.value); }
public static Integer operator -(Int16 x, Integer y) { return new Integer(x - y.value); }
public static Integer operator -(Int32 x, Integer y) { return new Integer(x - y.value); }
public static Integer operator -(Int64 x, Integer y) { return new Integer(x - y.value); }
public static Integer operator -(UInt16 x, Integer y) { return new Integer(x - y.value); }
public static Integer operator -(UInt32 x, Integer y) { return new Integer(x - y.value); }
public static Integer operator -(UInt64 x, Integer y) { return new Integer(x - y.value); }
public static Integer operator *(Int16 x, Integer y) { return new Integer(x * y.value); }
public static Integer operator *(Int32 x, Integer y) { return new Integer(x * y.value); }
public static Integer operator *(Int64 x, Integer y) { return new Integer(x * y.value); }
public static Integer operator *(UInt16 x, Integer y) { return new Integer(x * y.value); }
public static Integer operator *(UInt32 x, Integer y) { return new Integer(x * y.value); }
public static Integer operator *(UInt64 x, Integer y) { return new Integer(x * y.value); }
public static Integer operator /(Int16 x, Integer y) { return new Integer(x / y.value); }
public static Integer operator /(Int32 x, Integer y) { return new Integer(x / y.value); }
public static Integer operator /(Int64 x, Integer y) { return new Integer(x / y.value); }
public static Integer operator /(UInt16 x, Integer y) { return new Integer(x / y.value); }
public static Integer operator /(UInt32 x, Integer y) { return new Integer(x / y.value); }
public static Integer operator /(UInt64 x, Integer y) { return new Integer(x / y.value); }
public static Integer operator %(Int16 x, Integer y) { return new Integer(x % y.value); }
public static Integer operator %(Int32 x, Integer y) { return new Integer(x % y.value); }
public static Integer operator %(Int64 x, Integer y) { return new Integer(x % y.value); }
public static Integer operator %(UInt16 x, Integer y) { return new Integer(x % y.value); }
public static Integer operator %(UInt32 x, Integer y) { return new Integer(x % y.value); }
public static Integer operator %(UInt64 x, Integer y) { return new Integer(x % y.value); }
public static bool operator ==(Int16 x, Integer y) { return x == y.value; }
public static bool operator !=(Int16 x, Integer y) { return x != y.value; }
public static bool operator ==(Int32 x, Integer y) { return x == y.value; }
public static bool operator !=(Int32 x, Integer y) { return x != y.value; }
public static bool operator ==(Int64 x, Integer y) { return x == y.value; }
public static bool operator !=(Int64 x, Integer y) { return x != y.value; }
public static bool operator ==(UInt16 x, Integer y) { return x == y.value; }
public static bool operator !=(UInt16 x, Integer y) { return x != y.value; }
public static bool operator ==(UInt32 x, Integer y) { return x == y.value; }
public static bool operator !=(UInt32 x, Integer y) { return x != y.value; }
public static bool operator ==(UInt64 x, Integer y) { return x == y.value; }
public static bool operator !=(UInt64 x, Integer y) { return x != y.value; }
public static bool operator >(Int16 x, Integer y) { return x > y.value; }
public static bool operator <(Int16 x, Integer y) { return x < y.value; }
public static bool operator >(Int32 x, Integer y) { return x > y.value; }
public static bool operator <(Int32 x, Integer y) { return x < y.value; }
public static bool operator >(Int64 x, Integer y) { return x > y.value; }
public static bool operator <(Int64 x, Integer y) { return x < y.value; }
public static bool operator >(UInt16 x, Integer y) { return x > y.value; }
public static bool operator <(UInt16 x, Integer y) { return x < y.value; }
public static bool operator >(UInt32 x, Integer y) { return x > y.value; }
public static bool operator <(UInt32 x, Integer y) { return x < y.value; }
public static bool operator >(UInt64 x, Integer y) { return x > y.value; }
public static bool operator <(UInt64 x, Integer y) { return x < y.value; }
public static bool operator >=(Int16 x, Integer y) { return x >= y.value; }
public static bool operator <=(Int16 x, Integer y) { return x <= y.value; }
public static bool operator >=(Int32 x, Integer y) { return x >= y.value; }
public static bool operator <=(Int32 x, Integer y) { return x <= y.value; }
public static bool operator >=(Int64 x, Integer y) { return x >= y.value; }
public static bool operator <=(Int64 x, Integer y) { return x <= y.value; }
public static bool operator >=(UInt16 x, Integer y) { return x >= y.value; }
public static bool operator <=(UInt16 x, Integer y) { return x <= y.value; }
public static bool operator >=(UInt32 x, Integer y) { return x >= y.value; }
public static bool operator <=(UInt32 x, Integer y) { return x <= y.value; }
public static bool operator >=(UInt64 x, Integer y) { return x >= y.value; }
public static bool operator <=(UInt64 x, Integer y) { return x <= y.value; }
}
public static class Program
{
private static bool IntegerFunction(Integer n)
{
//code that implements IntegerFunction goes here
//note that there is NO code that checks the type of n in rum time, because it is NOT needed anymore
}
private static void Main()
{
Console.WriteLine("{0}",IntegerFunction(0)); //compile error: there is no overloaded METHOD for objects of type "int" and no implicit conversion from any object, including "int", to "Integer" is known.
Console.WriteLine("{0}",IntegerFunction(new Integer(0))); //both compiles and no run time error
Console.WriteLine("{0}",IntegerFunction("string")); //compile error: there is no overloaded METHOD for objects of type "string" and no implicit conversion from any object, including "string", to "Integer" is known.
Console.WriteLine("{0}",IntegerFunction(new Integer("string"))); //compile error: there is no overloaded CONSTRUCTOR for objects of type "string"
}
}
请注意,为了在代码中使用动态,必须添加对Microsoft.CSharp的引用
如果.NET Framework的版本低于/低于/低于4.0,并且在该版本中未定义dynamic,那么您将不得不使用object并强制转换为整数类型,这很麻烦,因此我建议您使用.NET 4.0或更高版本(如果可以),则可以使用动态代替对象。
不幸的是,.NET没有提供本地执行此操作的方法。
为了解决此问题,我创建了OSS库Genumerics,该库为以下内置数字类型及其可为空的等效项提供最标准的数字运算,并能够添加对其他数字类型的支持。
sbyte
,byte
,short
,ushort
,int
,uint
,long
,ulong
,float
,double
,decimal
,和BigInteger
性能等同于特定于数字类型的解决方案,使您可以创建有效的通用数字算法。
这是代码用法的示例。
public static T Sum(T[] items)
{
T sum = Number.Zero<T>();
foreach (T item in items)
{
sum = Number.Add(sum, item);
}
return sum;
}
public static T SumAlt(T[] items)
{
// implicit conversion to Number<T>
Number<T> sum = Number.Zero<T>();
foreach (T item in items)
{
// operator support
sum += item;
}
// implicit conversion to T
return sum;
}
练习的目的是什么?
正如人们已经指出的那样,您可能有一个非泛型函数来处理最大的项,并且编译器会自动为您转换较小的int。
static bool IntegerFunction(Int64 value) { }
如果您的功能位于性能至关重要的路径上(IMO不太可能),则可以为所有需要的功能提供重载。
static bool IntegerFunction(Int64 value) { }
...
static bool IntegerFunction(Int16 value) { }
我会使用一种通用的,您可以处理外部性...
/// <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;
}
当我尝试重载泛型类型的运算符时,这一限制影响了我。由于没有“数字”约束,并且由于许多其他原因,stackoverflow上的好人很乐意提供,因此无法在泛型类型上定义操作。
我想要类似的东西
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; };
}
}
我已经使用.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 };
}
}
关于使用的两件事dynamic
是
.NET数字基元类型不共享任何公共接口,这些接口将使它们无法用于计算。这将有可能定义自己的界面(例如ISignedWholeNumber
),这将执行这样的操作,定义包含一个单一的结构Int16
,Int32
等,以及实现这些接口,然后具有接受泛型类型约束的方法ISignedWholeNumber
,但不必转换数值对您的结构类型可能很麻烦。
另一种方法是定义静态类Int64Converter<T>
与静态属性bool Available {get;};
和静态代表Int64 GetInt64(T value)
,T FromInt64(Int64 value)
,bool TryStoreInt64(Int64 value, ref T dest)
。该类的构造函数可以使用硬编码来加载已知类型的委托,并可以使用Reflection来测试类型是否T
使用正确的名称和签名来实现方法(以防某种类似的结构,其中包含一个Int64
and表示一个数字,但是具有自定义ToString()
方法)。这种方法将失去与编译时类型检查相关的优势,但仍将设法避免装箱操作,并且每种类型仅需“检查”一次。之后,与该类型关联的操作将被委托分派替换。
Int64
结果,但是不提供一种方法,例如可以将任意类型的整数递增以产生另一个相同类型的整数。
在类似的情况下,我需要处理数字类型和字符串。似乎有点奇怪的混合,但是你去了。
再次,就像许多人一样,我查看了约束,并提出了它必须支持的一堆接口。但是,a)并非100%是水密的,b)任何新查看此长条约束的人都会立即感到困惑。
因此,我的方法是将我所有的逻辑都放到不受约束的通用方法中,而是将该通用方法设为私有。然后,我使用公共方法公开它,其中一个显式处理我想要处理的类型-在我看来,代码是干净且显式的,例如
public static string DoSomething(this int input, ...) => DoSomethingHelper(input, ...);
public static string DoSomething(this decimal input, ...) => DoSomethingHelper(input, ...);
public static string DoSomething(this double input, ...) => DoSomethingHelper(input, ...);
public static string DoSomething(this string input, ...) => DoSomethingHelper(input, ...);
private static string DoSomethingHelper<T>(this T input, ....)
{
// complex logic
}
如果只需要使用一种数字类型,则可以考虑使用来创建类似于C ++中别名的内容using
。
因此,与其拥有非常通用的
T ComputeSomething<T>(T value1, T value2) where T : INumeric { ... }
你可以有
using MyNumType = System.Double;
T ComputeSomething<MyNumType>(MyNumType value1, MyNumType value2) { ... }
这可能让你轻松地从去double
到int
,或者如果别人需要的,但你将无法使用ComputeSomething
与double
和int
在同一个程序。
但是,为什么不全部更换double
到int
呢?因为您的方法可能要使用a double
作为输入是double
还是int
。别名使您可以准确知道哪个变量使用了动态类型。
主题很旧,但对于将来的读者:
Discriminated Unions
到目前为止,此功能与C#中尚未实现的功能紧密相关。我在这里找到了问题:
https://github.com/dotnet/csharplang/issues/113
此问题仍未解决,已经计划了该功能 C# 10
因此,我们仍然需要等待更多时间,但是在释放之后,您可以通过以下方式进行操作:
static bool IntegerFunction<T>(T value) where T : Int16 | Int32 | Int64 | ...
我认为您是对仿制药的误解。如果您尝试执行的操作仅对特定数据类型有用,那么您就没有做“泛型”的事情。
另外,由于您只希望允许该函数在int数据类型上工作,因此您不需要为每个特定大小使用单独的函数。只需采用最大特定类型的参数,程序便可以自动将较小的数据类型转换为该参数。(即,调用时传递Int16将自动转换为Int64)。
如果您根据传递给函数的int的实际大小执行不同的操作,那么我认为即使尝试做您正在做的事情,您也应该认真考虑一下。如果您必须愚弄语言,则应多想一些您想完成的事情,而不是如何做自己想做的事情。
如果没有其他方法,则可以使用Object类型的参数,然后必须检查参数的类型并采取适当的操作或引发异常。