C# 泛型 - 基于类型特征的重载
C# generics - overload based on type traits
我是 C# 的新手,我想创建一个接受任何数字参数的函数,无论是 int/float、有符号/无符号、短/长等,以及另一个接受任何其他类型的函数。
C++这可以使用SFINAE和std::is_arithmetic
轻松完成:
template<typename T>
typename std::enable_if<std::is_arithmetic<T>::value>::type DoSomething(T)
{
std::cout<<"int/float/char..."<<std::endl;
}
template<typename T>
typename std::enable_if<!std::is_arithmetic<T>::value>::type DoSomething(T)
{
std::cout<<"any other type except int/float..."<<std::endl;
}
如何在 C# 中实现类似的行为?我不想为每个数字类型创建一个重载作为所有数字类型的代码,因为代码完全相同。
你不能在 C# 中执行此操作。您要么定义所有可能的重载,要么选择可以容纳(精确或不精确(所有预期值的"更大"类型。
这是一个经常出现的问题。阅读这个或这个,以获取有关该主题的非常知识渊博的信息。
我已经能够想出一些符合你正在寻找的东西;它远不如在C++中可以做到的那么优雅。
它的根源是一个struct
看起来像
namespace TypeTraits
{
struct Number<T, TOperations> : IComparable, IFormattable, IConvertible, IComparable<Number<T, TOperations>>, IEquatable<Number<T, TOperations>>, IComparable<T>, IEquatable<T>
where T : IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
where TOperations : Operations<Number<T, TOperations>>, new()
{
static readonly Operations<Number<T, TOperations>> Operations = new TOperations();
public T Value { get; }
public Number(T value)
{
Value = value;
}
public static implicit operator Number<T, TOperations>(T source) => new Number<T, TOperations>(source);
public static implicit operator T(Number<T, TOperations> source) => source.Value;
public override bool Equals(object obj) => Value.Equals(obj);
public override int GetHashCode() => Value.GetHashCode();
public override string ToString() => Value.ToString();
public bool Equals(T other) => Value.Equals(other);
public bool Equals(Number<T, TOperations> other) => Equals(other.Value);
public int CompareTo(object obj) => Value.CompareTo(obj);
public int CompareTo(T other) => Value.CompareTo(other);
public int CompareTo(Number<T, TOperations> other) => CompareTo(other.Value);
public string ToString(string format, IFormatProvider formatProvider) => Value.ToString(format, formatProvider);
public TypeCode GetTypeCode() => Value.GetTypeCode();
public bool ToBoolean(IFormatProvider provider) => Value.ToBoolean(provider);
public byte ToByte(IFormatProvider provider) => Value.ToByte(provider);
public char ToChar(IFormatProvider provider) => Value.ToChar(provider);
public DateTime ToDateTime(IFormatProvider provider) => Value.ToDateTime(provider);
public decimal ToDecimal(IFormatProvider provider) => Value.ToDecimal(provider);
public double ToDouble(IFormatProvider provider) => Value.ToDouble(provider);
public short ToInt16(IFormatProvider provider) => Value.ToInt16(provider);
public int ToInt32(IFormatProvider provider) => Value.ToInt32(provider);
public long ToInt64(IFormatProvider provider) => Value.ToInt64(provider);
public sbyte ToSByte(IFormatProvider provider) => Value.ToSByte(provider);
public float ToSingle(IFormatProvider provider) => Value.ToSingle(provider);
public string ToString(IFormatProvider provider) => Value.ToString(provider);
public object ToType(Type conversionType, IFormatProvider provider) => Value.ToType(conversionType, provider);
public ushort ToUInt16(IFormatProvider provider) => Value.ToUInt16(provider);
public uint ToUInt32(IFormatProvider provider) => Value.ToUInt32(provider);
public ulong ToUInt64(IFormatProvider provider) => Value.ToUInt64(provider);
public static Number<T, TOperations> operator+(Number<T, TOperations> lhs, Number<T, TOperations> rhs) => Operations.Add(lhs, rhs);
}
}
是的,这是很多代码;但其中大部分是简单的传递。
然后,您有一个抽象的Operations
类和几个具体的实现。 这是将事物放入类型中的"技巧",以便它可以用作泛型参数。
namespace TypeTraits
{
class Operations<T>
where T : IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
{
// names from https://msdn.microsoft.com/en-us/library/ms182355.aspx
public virtual T Add(T a, T b)
{
throw new NotImplementedException();
}
}
sealed class OperationsInt32 : Operations<Int32>
{
public override Int32 Add(Int32 a, Int32 b)
{
return a.Value + b.Value;
}
}
sealed class OperationsDouble : Operations<Double>
{
public override Double Add(Double a, Double b)
{
return a.Value + b.Value;
}
}
}
最后,在顶层(namespace
之外(,一些using
别名
using Int32 = TypeTraits.Number<System.Int32, TypeTraits.OperationsInt32>;
using Double = TypeTraits.Number<System.Double, TypeTraits.OperationsDouble>;
完成所有这些操作后,您现在可以编写如下代码:
class Program
{
static void DoSomething<T>(T t)
{
Console.WriteLine("DoSomething<T>");
}
static void DoSomething<T, TOperations>(Number<T, TOperations> t)
where T : IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
where TOperations : Operations<Number<T, TOperations>>, new()
{
Number<T, TOperations> t2 = t;
var t3 = t + t2;
Console.WriteLine("DoSomething<Number<T, TOperations>>");
}
static void Main(string[] args)
{
string s = "314";
Int32 i = 314;
Double d = 3.14;
DoSomething(s);
DoSomething(i);
DoSomething(d);
}
}
也许可以通过消除一些约束来简化这一点......
相关文章:
- 根据C++标准的定义实现"is_similar"类型特征
- C++类型特征,以查看是否可以<uint32_t>对类型"K"的任何变量调用"static_cast(k)"
- C ++类型特征:确保子类实现方法
- 根据类型特征更改函数定义?
- 如何使用类型特征将函数的通用引用参数限制为 r 值引用?
- 在C++中创建新的类型特征
- 如何构造一个类型特征,可以判断一个类型的私有方法是否可以在另一个类型的构造函数中调用?
- std::begin-类型特征中未考虑用户定义的重载
- 在类型特征修改后,无法限定类型
- 使用类型特征的部分类专用化
- 为什么不调用预期的函数?我是否对类型特征的理解不正确?
- 了解类型特征的体系结构
- 为什么使用某些类型特征的模板类型会导致模板扣除失败
- 有了C++类型特征,有没有办法避免铸造
- 哪种类型特征表明该类型是可分配的?(元组,对)
- 一种类型特征,标识哪个类提供通过重载解析选择的函数
- 如何使用类型特征正确推断引用的参数
- 如何通过扩展以下类型特征来删除 decltype(&MyClass::funct) 部分?
- 从标准库类型特征继承
- 为什么类型特征不适用于命名空间范围内的类型?