是否可以像使用 C++ 模板一样在 C# 泛型中实现基本算术(至少是加法)?我一直在尝试让它们正常工作,但 C# 不允许您多次声明相同的泛型类型,就像使用模板一样。
广泛的谷歌搜索没有提供答案。
编辑:谢谢,但我正在寻找一种在编译时进行算术运算的方法,在泛型类型中嵌入诸如 Church 数字之类的东西。这就是我链接我所做的文章的原因。泛型类型的算术,而不是泛型类型实例的算术。
是否可以像使用 C++ 模板一样在 C# 泛型中实现基本算术(至少是加法)?我一直在尝试让它们正常工作,但 C# 不允许您多次声明相同的泛型类型,就像使用模板一样。
广泛的谷歌搜索没有提供答案。
编辑:谢谢,但我正在寻找一种在编译时进行算术运算的方法,在泛型类型中嵌入诸如 Church 数字之类的东西。这就是我链接我所做的文章的原因。泛型类型的算术,而不是泛型类型实例的算术。
不幸的是,您不能对泛型类型使用算术运算
T Add(T a, T b)
{
return a + b; // compiler error here
}
在 c# 中不起作用!
但是您可以创建自己的数字类型并重载运算符(算术、相等和implicit
, explicit
)。这使您可以非常自然地使用它们。但是,您不能使用泛型创建继承层次结构。您将不得不使用非泛型基类或接口。
我只是用矢量类型做的。这里有一个缩短的版本:
public class Vector
{
private const double Eps = 1e-7;
public Vector(double x, double y)
{
_x = x;
_y = y;
}
private double _x;
public double X
{
get { return _x; }
}
private double _y;
public double Y
{
get { return _y; }
}
public static Vector operator +(Vector a, Vector b)
{
return new Vector(a._x + b._x, a._y + b._y);
}
public static Vector operator *(double d, Vector v)
{
return new Vector(d * v._x, d * v._y);
}
public static bool operator ==(Vector a, Vector b)
{
if (ReferenceEquals(a, null)) {
return ReferenceEquals(b, null);
}
if (ReferenceEquals(b, null)) {
return false;
}
return Math.Abs(a._x - b._x) < Eps && Math.Abs(a._y - b._y) < Eps;
}
public static bool operator !=(Vector a, Vector b)
{
return !(a == b);
}
public static implicit operator Vector(double[] point)
{
return new Vector(point[0], point[1]);
}
public static implicit operator Vector(PointF point)
{
return new Vector(point.X, point.Y);
}
public override int GetHashCode()
{
return _x.GetHashCode() ^ _y.GetHashCode();
}
public override bool Equals(object obj)
{
var other = obj as Vector;
return other != null && Math.Abs(other._x - _x) < Eps && Math.Abs(other._y - _y) < Eps;
}
public override string ToString()
{
return String.Format("Vector({0:0.0000}, {1:0.0000})", _x, _y);
}
}
如果我的回答似乎不合时宜,请随时提供更多说明。
至少 C# 语言中的运算符没有通用约束。正如 Jon Skeet 用Unconstrained Melody证明的那样,约束实际上可能在 CLR 本身中完全有效。
您可以对约束做的最好的事情是提供接口/自定义类来公开您需要的操作。您将无法提供原语(除非您implicit
可能还实现了运算符),但它至少可以让您为数学部分创建通用代码。
通用约束允许编译器根据最小公分母(由约束或缺少的指定)推断可用成员。大多数时候,泛型是不受约束的,因此只给你object
语义。
dynamic
来临时存储泛型变量,然后假设(通过鸭子类型)它具有相关的运算符:
class Program
{
static void Main(string[] args)
{
var result = Add<int, long, float>(1, 2);
Console.WriteLine(result); // 3
Console.WriteLine(result.GetType().FullName); // System.Single
Console.Read();
}
static T3 Add<T1, T2, T3>(T1 left, T2 right)
{
dynamic d1 = left;
dynamic d2 = right;
return (T3)(d1 + d2);
}
}
这涉及 DLR 并且会产生一些性能开销(我没有确切的数字),特别是如果您希望计算对性能至关重要。
class Tuple<T1, T2> // etc.
var myTuple = new Tuple<int, int>(1, 2);
朋友们,在 C# 中对此的直观答案是 RTTI 并从对象类来回转换
enter code here
class MyMath
{
public static T Add<T>(T a, T b) where T: struct
{
switch (typeof(T).Name)
{
case "Int32":
return (T) (object)((int)(object)a + (int)(object)b);
case "Double":
return (T)(object)((double)(object)a + (double)(object)b);
default:
return default(T);
}
}
}
class Program
{
public static int Main()
{
Console.WriteLine(MyMath.Add<double>(3.6, 2.12));
return 0;
}
}
是的,它可以通过使用动态类型变量来完成。
例子:
T Add(T value1, T value2)
{
dynamic a = value1;
dynamic b = value2;
return (a + b);
}
更多参考请点击这里
它来了!
查看此.NET 博客文章。
从 .NET 6 开始,如果您愿意启用语言预览功能,则可以启用对此的支持。这意味着无法保证该功能将在更高版本的框架(.NET 7+)中存在(或以相同的方式工作)。
如果您对此感到满意,请继续阅读以了解如何为您的项目启用此功能。您将能够使用接口,例如INumber
并IFloatingPoint
创建程序,例如:
using System;
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;
}
INumber
目前来自System.Runtime.Experimental
NuGet 包。我上面示例的项目文件看起来像
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<EnablePreviewFeatures>true</EnablePreviewFeatures>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>preview</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Runtime.Experimental" Version="6.0.0" />
</ItemGroup>
</Project>
还有诸如IAdditionOperators
和之类的接口,IComparisonOperators
因此您可以通用地使用特定的运算符。
老实说,我已经等待了很长时间才能得到支持。
关于与和泛型的算术Enums
相关的答案。
如果您正在专门寻找按位运算符(例如 '&', |
),^
将 withEnums
用作通用约束;IConveritible
除了作为约束之外添加是System.Enum
一种可行的解决方法。
public static bool Contains<T>(this T container, T Value) where T : Enum, IConvertible
{
return (container.ToInt32(null) & Value.ToInt32(null)) != 0;
}
public static T Insert<T>(this ref T container, T Value) where T : struct, Enum, IConvertible
{
// this is slow and a proof of concept, not recommend for frequent use
container = (T)Enum.ToObject(typeof(T), container.ToInt32(null) | Value.ToInt32(null));
return container;
}