C# 通用运算符 - RTTI 方法
C# generic operators - RTTI approach
我打算询问泛型 operator+ 重载,但不是典型的 "can I do operator+ for generic type" 方式。
问题在最下面
我最近开始在 C# 中创建矩阵 class,过了一段时间我发现我不能做简单的 T + T!
因此,我在谷歌上搜索了又搜索,找到了几个解决方法。
- 创建表达式link
- 创建摘要 class
abstract class Matrix<T>{//some code}
。创建 'protected virtual method Add(T itemToAdd)' 然后创建
像这样的运算符:T operator+(T item1, T item2){return item1.Add(item2);}
(堆栈上的大多数帖子)然后在 class Matrix : Matrix<int>
here 中继承此方法
- 使用添加方法,例如:
T Add(T first, T second){ dynamic output = first + second; return output;}
(堆栈中的某处)
第一个不适合我,所以我尝试了第二个,但后来 运行 遇到了严重的问题,例如:
- (大量)重复代码 - 我创建了 4 个 classes 用于:int、double、long、Complex - 我自己的类型
- 创建多个扩展方法等。
第三个太不安全了,我直接拒绝了。
经过我的努力,我开始意识到:“为什么我不使用 RTTI 和反射?”我知道,这在 运行ning 时间很昂贵,但为什么不使用静态构造函数来做到这一点呢?
这是我的想法(伪代码):
class Matrix<T>{
static Func<T,T,T> Add;
static Matrix
{
if(T is int)
Add = (first,second) = > ((int)first)+((int)second);
else if(T is long)
Add = (first,second) = > ((long)first)+((long)second);
// and so on for built-in types
else
{ // T is not built-in type
if(typeof(T).GetMethods().Contains("op_Addition"))
{
Add = (first,second) => typeof(T).getMethod("op_Addition").invoke(first,second);
}
}
}
我知道反射的成本很高,但它只会执行一次(每种类型)!
在你开始争论之前:我要像这样编写 T is int
代码:
var type = typeof(T);
if(type==typeof(int)) // code
我知道我不能将 T 显式转换为 int,但必须有某种 'work around'。问题是(例如)Int32 对于 operator+ 没有明确的 'method' 因此,反射没有多大用处。
介绍完之后我有两个问题:
- 这是一个好方法还是您认为它存在重大缺陷?
- 可行吗?我不想在不确定我的 lambda 函数是否有效的情况下开始创建代码。
编辑 1+2
我将代码更改为通用代码。
我想也许你需要使用我的 class,给你:
Matrix<int> matrix = new Matrix(1,1); // creates int-based matrix
Matrix<MyClass> matrix2 = new Matrix(1,1); // creates some other type matrix
回答
根据 dasblinkenlight 的回答,我设法做到了:
public interface ITypeTratis<T>
{
T Add(T a, T b);
T Mul(T a, T b);
T Sub(T a, T b);
T Div(T a, T b);
bool Eq(T a, T b);
}
public class IntTypeTratis : ITypeTratis<int>
{
//code for int
}
public class DoubleTypeTratis : ITypeTratis<double>
{
//code for double
}
internal class TypeTraits<T> : ITypeTratis<T>
{
public Func<T, T, T> AddF;
public Func<T, T, T> MulF;
public Func<T, T, T> DivF;
public Func<T, T, T> SubF;
public Func<T, T, bool> EqF;
public T Add(T a, T b) => AddF(a, b);
public bool Eq(T a, T b) => EqF(a, b);
public T Mul(T a, T b) => MulF(a, b);
public T Sub(T a, T b) => SubF(a, b);
public T Div(T a, T b) => DivF(a, b);
}
public class Matrix<T>
{
private static IDictionary<Type, object> traitByType = new Dictionary<Type, object>()
{
{typeof (int), new IntTypeTratis()},
{typeof (double), new DoubleTypeTratis()}
};
static Matrix()
{
Debug.WriteLine("Robie konstruktor dla " + typeof(T));
var type = typeof(T);
if (!traitByType.ContainsKey(type))
{
MethodInfo add, sub, mul, div, eq;
if ((add = type.GetMethod("op_Addition")) == null)
throw new NotSupportedException("Addition is not implemented");
if ((sub = type.GetMethod("op_Subtraction")) == null)
throw new NotSupportedException("Substraction is not implemented");
if ((mul = type.GetMethod("op_Multiply")) == null)
throw new NotSupportedException("Multiply is not implemented");
if ((div = type.GetMethod("op_Division")) == null)
throw new NotSupportedException("Division is not implemented");
if ((eq = type.GetMethod("op_Equality")) == null)
throw new NotSupportedException("Equality is not implemented");
var obj = new TypeTraits<T>
{
AddF = (a, b) => (T)add.Invoke(null, new object[] { a, b }),
SubF = (a, b) => (T)sub.Invoke(null, new object[] { a, b }),
MulF = (a, b) => (T)mul.Invoke(null, new object[] { a, b }),
DivF = (a, b) => (T)div.Invoke(null, new object[] { a, b }),
EqF = (a, b) => (bool)eq.Invoke(null, new object[] { a, b })
};
traitByType[type] = obj;
}
}
}
这正是我要找的。
是的,你的方法会很好。
您的静态构造函数将为每个类型参数 运行 T
,确保正确设置 Add
。
您可能希望将加法逻辑分离到矩阵外部的单独 class 中,并根据矩阵的类型使用 class 到 运行 操作。例如,如果您还需要乘法,您可以构建一个 ITypeTraits<T>
接口,其中包含 Add
和 Multiply
:
public interface ITypeTraits<T> {
T Add(T a, T b);
T Mul(T a, T b);
}
现在您可以为单个类型构建 ITypeTraits<T>
的实现,例如
public class IntTypeTraits : ITypeTraits<int> {
public int Add(int a, int b) { return a+b; }
public int Mul(int a, int b) { return a*b; }
}
public class LongTypeTraits : ITypeTraits<long> {
public long Add(long a, long b) { return a+b; }
public long Mul(long a, long b) { return a*b; }
}
... // and so on
把它们编成字典
static readonly IDictionary<Type,object> traitByType = new Dictionary<Type,object> {
{typeof(int), new IntTypeTraits() }
, {typeof(long), new LongTypeTraits() }
... // and so on
};
并获取执行操作所需的那个:
ITypeTraits<T> traits = (ITypeTraits<T>)traitByType(typeof(T));
T first = ...
T second = ...
T sum = traits.Add(first, second);
T prod = traits.Mul(first, second);
#3 有什么问题?您可以只检查类型,如下所示:
public abstract class Matrix<T>
{
public static HashSet<Type> AllowAdd = new HashSet<Type>
{
typeof(int),
typeof(long),
typeof(string),
typeof(double),
};
public T Add<T>(T first, T second)
{
if(!AllowAdd.Contains(typeof(T)))
{
throw new Exception(string.Format("Cannot preform addition for type: {0}", typeof(T).Name));
}
dynamic result = (dynamic)first + (dynamic)second;
return (T)result;
}
}
在 上构建,这是我的版本。好处是它不需要字典查找,而是让类型系统来做。我认为应该更快,但我还没有测量过。也少打字了。
public abstract class MatrixBase
{
protected static class OperationDict<T>
{
private static Func<T,T,T> _notSupported = (a, b) => { throw new NotSupportedException(string.Format("Type {0} not supported for Matrix operations!", typeof(T))); };
public static Func<T, T, T> Add = _notSupported;
public static Func<T, T, T> Multiply = _notSupported;
}
static MatrixBase()
{
OperationDict<int>.Add = (a, b) => a + b;
OperationDict<int>.Multiply = (a, b) => a * b;
OperationDict<decimal>.Add = (a, b) => a + b;
OperationDict<decimal>.Multiply = (a, b) => a * b;
// Etc. for all supported types
}
}
public class Matrix<T> : MatrixBase
{
public T DoAdd(T a, T b)
{
return OperationDict<T>.Add(a, b);
}
}
我认为您走在正确的道路上,为了避免使用反射,您需要以某种方式通知编译器您知道 "T" 具有“+”运算符,但是,this feature does not yet exist 在 C# 中,因此如果不进行运行时类型检查或施加其他约束,这是不可能实现的。
如果你不关心性能,你可以使用 dynamic
:
(dynamic)first + (dynamic)second
但这将在每次操作中产生多次反射性能影响
或者您可以使用其他一些更复杂的方法,将特定方法缓存在字典中,但您至少无法避免在 add
的实现中调用 .GetType()
[=15] =]
我打算询问泛型 operator+ 重载,但不是典型的 "can I do operator+ for generic type" 方式。
问题在最下面
我最近开始在 C# 中创建矩阵 class,过了一段时间我发现我不能做简单的 T + T!
因此,我在谷歌上搜索了又搜索,找到了几个解决方法。
- 创建表达式link
- 创建摘要 class
abstract class Matrix<T>{//some code}
。创建 'protected virtual method Add(T itemToAdd)' 然后创建 像这样的运算符:T operator+(T item1, T item2){return item1.Add(item2);}
(堆栈上的大多数帖子)然后在class Matrix : Matrix<int>
here 中继承此方法
- 使用添加方法,例如:
T Add(T first, T second){ dynamic output = first + second; return output;}
(堆栈中的某处)
第一个不适合我,所以我尝试了第二个,但后来 运行 遇到了严重的问题,例如:
- (大量)重复代码 - 我创建了 4 个 classes 用于:int、double、long、Complex - 我自己的类型
- 创建多个扩展方法等。
第三个太不安全了,我直接拒绝了。
经过我的努力,我开始意识到:“为什么我不使用 RTTI 和反射?”我知道,这在 运行ning 时间很昂贵,但为什么不使用静态构造函数来做到这一点呢?
这是我的想法(伪代码):
class Matrix<T>{
static Func<T,T,T> Add;
static Matrix
{
if(T is int)
Add = (first,second) = > ((int)first)+((int)second);
else if(T is long)
Add = (first,second) = > ((long)first)+((long)second);
// and so on for built-in types
else
{ // T is not built-in type
if(typeof(T).GetMethods().Contains("op_Addition"))
{
Add = (first,second) => typeof(T).getMethod("op_Addition").invoke(first,second);
}
}
}
我知道反射的成本很高,但它只会执行一次(每种类型)!
在你开始争论之前:我要像这样编写 T is int
代码:
var type = typeof(T);
if(type==typeof(int)) // code
我知道我不能将 T 显式转换为 int,但必须有某种 'work around'。问题是(例如)Int32 对于 operator+ 没有明确的 'method' 因此,反射没有多大用处。
介绍完之后我有两个问题:
- 这是一个好方法还是您认为它存在重大缺陷?
- 可行吗?我不想在不确定我的 lambda 函数是否有效的情况下开始创建代码。
编辑 1+2 我将代码更改为通用代码。 我想也许你需要使用我的 class,给你:
Matrix<int> matrix = new Matrix(1,1); // creates int-based matrix
Matrix<MyClass> matrix2 = new Matrix(1,1); // creates some other type matrix
回答 根据 dasblinkenlight 的回答,我设法做到了:
public interface ITypeTratis<T>
{
T Add(T a, T b);
T Mul(T a, T b);
T Sub(T a, T b);
T Div(T a, T b);
bool Eq(T a, T b);
}
public class IntTypeTratis : ITypeTratis<int>
{
//code for int
}
public class DoubleTypeTratis : ITypeTratis<double>
{
//code for double
}
internal class TypeTraits<T> : ITypeTratis<T>
{
public Func<T, T, T> AddF;
public Func<T, T, T> MulF;
public Func<T, T, T> DivF;
public Func<T, T, T> SubF;
public Func<T, T, bool> EqF;
public T Add(T a, T b) => AddF(a, b);
public bool Eq(T a, T b) => EqF(a, b);
public T Mul(T a, T b) => MulF(a, b);
public T Sub(T a, T b) => SubF(a, b);
public T Div(T a, T b) => DivF(a, b);
}
public class Matrix<T>
{
private static IDictionary<Type, object> traitByType = new Dictionary<Type, object>()
{
{typeof (int), new IntTypeTratis()},
{typeof (double), new DoubleTypeTratis()}
};
static Matrix()
{
Debug.WriteLine("Robie konstruktor dla " + typeof(T));
var type = typeof(T);
if (!traitByType.ContainsKey(type))
{
MethodInfo add, sub, mul, div, eq;
if ((add = type.GetMethod("op_Addition")) == null)
throw new NotSupportedException("Addition is not implemented");
if ((sub = type.GetMethod("op_Subtraction")) == null)
throw new NotSupportedException("Substraction is not implemented");
if ((mul = type.GetMethod("op_Multiply")) == null)
throw new NotSupportedException("Multiply is not implemented");
if ((div = type.GetMethod("op_Division")) == null)
throw new NotSupportedException("Division is not implemented");
if ((eq = type.GetMethod("op_Equality")) == null)
throw new NotSupportedException("Equality is not implemented");
var obj = new TypeTraits<T>
{
AddF = (a, b) => (T)add.Invoke(null, new object[] { a, b }),
SubF = (a, b) => (T)sub.Invoke(null, new object[] { a, b }),
MulF = (a, b) => (T)mul.Invoke(null, new object[] { a, b }),
DivF = (a, b) => (T)div.Invoke(null, new object[] { a, b }),
EqF = (a, b) => (bool)eq.Invoke(null, new object[] { a, b })
};
traitByType[type] = obj;
}
}
}
这正是我要找的。
是的,你的方法会很好。
您的静态构造函数将为每个类型参数 运行 T
,确保正确设置 Add
。
您可能希望将加法逻辑分离到矩阵外部的单独 class 中,并根据矩阵的类型使用 class 到 运行 操作。例如,如果您还需要乘法,您可以构建一个 ITypeTraits<T>
接口,其中包含 Add
和 Multiply
:
public interface ITypeTraits<T> {
T Add(T a, T b);
T Mul(T a, T b);
}
现在您可以为单个类型构建 ITypeTraits<T>
的实现,例如
public class IntTypeTraits : ITypeTraits<int> {
public int Add(int a, int b) { return a+b; }
public int Mul(int a, int b) { return a*b; }
}
public class LongTypeTraits : ITypeTraits<long> {
public long Add(long a, long b) { return a+b; }
public long Mul(long a, long b) { return a*b; }
}
... // and so on
把它们编成字典
static readonly IDictionary<Type,object> traitByType = new Dictionary<Type,object> {
{typeof(int), new IntTypeTraits() }
, {typeof(long), new LongTypeTraits() }
... // and so on
};
并获取执行操作所需的那个:
ITypeTraits<T> traits = (ITypeTraits<T>)traitByType(typeof(T));
T first = ...
T second = ...
T sum = traits.Add(first, second);
T prod = traits.Mul(first, second);
#3 有什么问题?您可以只检查类型,如下所示:
public abstract class Matrix<T>
{
public static HashSet<Type> AllowAdd = new HashSet<Type>
{
typeof(int),
typeof(long),
typeof(string),
typeof(double),
};
public T Add<T>(T first, T second)
{
if(!AllowAdd.Contains(typeof(T)))
{
throw new Exception(string.Format("Cannot preform addition for type: {0}", typeof(T).Name));
}
dynamic result = (dynamic)first + (dynamic)second;
return (T)result;
}
}
在
public abstract class MatrixBase
{
protected static class OperationDict<T>
{
private static Func<T,T,T> _notSupported = (a, b) => { throw new NotSupportedException(string.Format("Type {0} not supported for Matrix operations!", typeof(T))); };
public static Func<T, T, T> Add = _notSupported;
public static Func<T, T, T> Multiply = _notSupported;
}
static MatrixBase()
{
OperationDict<int>.Add = (a, b) => a + b;
OperationDict<int>.Multiply = (a, b) => a * b;
OperationDict<decimal>.Add = (a, b) => a + b;
OperationDict<decimal>.Multiply = (a, b) => a * b;
// Etc. for all supported types
}
}
public class Matrix<T> : MatrixBase
{
public T DoAdd(T a, T b)
{
return OperationDict<T>.Add(a, b);
}
}
我认为您走在正确的道路上,为了避免使用反射,您需要以某种方式通知编译器您知道 "T" 具有“+”运算符,但是,this feature does not yet exist 在 C# 中,因此如果不进行运行时类型检查或施加其他约束,这是不可能实现的。
如果你不关心性能,你可以使用 dynamic
:
(dynamic)first + (dynamic)second
但这将在每次操作中产生多次反射性能影响
或者您可以使用其他一些更复杂的方法,将特定方法缓存在字典中,但您至少无法避免在 add
的实现中调用 .GetType()
[=15] =]