通用索引器重载特化
Generic indexer overload specialization
我知道 C# 中没有模板专业化,但这行得通(至少在编译时):
public T test0<T>()
{
return default(T);
}
// handling T=float case
public float test0()
{
return 0;
}
这甚至无法编译:
public T this[int i]
{
get { return default(T); }
set { }
}
public float this[int i]
{
get { return 0; }
set { }
}
也不是这个
public float this<float>[int i]
{
get { return 0; }
set { }
}
它说“.. 已经在浮动版本上定义了一个名为 'this' 的成员,其参数类型相同”。我可以检查通用 []
访问器中的所有类型,但是过多的 if-elseif
s 和 typeof()
s 会降低性能(我打算将其用作非托管数组而不是托管数组array) 和 dynamic
不是一个选项,因为我主要使用 .Net 3.5。在另一个问题中,我了解到我无法获取泛型类型的指针,因此我决定对每个浮点数、双精度数、整数和字节类型使用多个重载,并在此数组访问重载中使用这些重载(但不是在同一方法中但不同的不会失去性能。)
泛型 []
可以被某些原始类型重载,例如 float
吗?更具体地说,class 是否可以像下面的例子那样单独使用?
MyArr<float> arr = new MyArr<float>();
// specifically rounds to nearest for floats
arr[100]+=500f;
MyArr<int> arr = new MyArr<int>();
// specifically adds 1 to some performance counter variable
arr[100]+=500;
MyArr<byte> arr = new MyArr<byte>();
// does nothing special
arr[100]+=50;
如果答案是否定的,那么我将使用一个额外的 Interface
来实现此功能,但我不确定是否可以只为单个功能添加另一个接口到项目中。 (我应该在另一个问题中问 "is adding another interface just for a single method okay?" 吗?)
我认为以下内容可能会有所帮助:
public class GenericType<T>
{
public virtual T Test0()
{
return default(T);
}
public virtual T this[int i]
{
get { return default(T); }
set { }
}
}
public class FloatType : GenericType<float>
{
public override float Test0()
{
return 0;
}
public override float this[int i]
{
get { return 0; }
set { }
}
}
GenericType<float> nonOptimizedFloat = new GenericType<float>();
var defVal = nonOptimizedFloat[3]; // will use the non-optimized version
GenericType<float> optimizedFloat = new FloatType();
defVal = optimizedFloat[3]; // will use the optimized version
您可以拥有任意数量的优化类型,并且仍然在基础中保留一些通用逻辑 class。
您也可以考虑将基础 class 定义为抽象的,并确保始终使用优化版本。
您可以使用包含 nested generic class
和 Func/Action
委托的 helper class
来实现 Indexer/Property/Method.
的特化
internal static class IndexerImpl
{
private static T IndexerDefaultImpl<T>(int i) => default(T); //default implementation
private static T IndexerImpl2<T>(int i) => default(T); //another implementation for short/int/long
private static string IndexerForString(int i) => (i * i).ToString(); //specialization for T=string
private static DateTime IndexerForDateTime(int i) => new DateTime(i * i * i); //specialization for T=DateTime
static IndexerImpl() //install the specializations
{
Specializer<string>.Fun = IndexerForString;
Specializer<DateTime>.Fun = IndexerForDateTime;
Specializer<short>.Fun = IndexerImpl2<short>;
Specializer<int>.Fun = IndexerImpl2<int>;
Specializer<long>.Fun = IndexerImpl2<long>;
}
internal static class Specializer<T> //specialization dispatcher
{
internal static Func<int, T> Fun;
internal static T Call(int i)
=> null != Fun
? Fun(i)
: IndexerDefaultImpl<T>(i);
}
}
public class YourClass<T>
{
public T this[int i] => IndexerImpl.Specializer<T>.Call(i);
}
如果你需要YourClass
的实例来计算return值,你可以添加一个参数来传递需要的信息:
internal static class IndexerImpl
{
private static T IndexerDefaultImpl<T>(int i, YourClass<T> yourClass) => default(T); //default implementation
private static T IndexerImpl2<T>(int i, YourClass<T> yourClass) => default(T); //another implementation for short/int/long
private static string IndexerForString<T>(int i, YourClass<T> yourClass) => (i * i).ToString(); //specialization for T=string
private static DateTime IndexerForDateTime<T>(int i, YourClass<T> yourClass) => new DateTime(i * i * i); //specialization for T=DateTime
static IndexerImpl() //install the specializations
{
Specializer<string>.Fun = IndexerForString;
Specializer<DateTime>.Fun = IndexerForDateTime;
Specializer<short>.Fun = IndexerImpl2;
Specializer<int>.Fun = IndexerImpl2;
Specializer<long>.Fun = IndexerImpl2;
}
internal static class Specializer<T> //specialization dispatcher
{
internal static Func<int, YourClass<T>, T> Fun;
internal static T Call(int i, YourClass<T> yourClass)
=> null != Fun
? Fun(i, yourClass)
: IndexerDefaultImpl(i, yourClass);
}
}
public class YourClass<T>
{
public T this[int i] => IndexerImpl.Specializer<T>.Call(i, this);
}
在专门化通用索引器时,需要 非通用 助手 class。
当专门化非泛型 class 的泛型 Property/Method 时,default implementation
/specializations
和 nested specialization dispatcher class
可以直接放在你的 class.
你要注意的是,不要把特化安装到 generic class.
的静态构造函数中
此外,如果您需要部分特化,您可以使用通用助手 class,其参数化类型不需要特化:
internal static class GetValueImpl<R, S>
{
private static T DefImpl<T>(R r, S s) => default(T);
private static int IntRet(R r, S s) => int.MaxValue;
internal static class Specializer<T>
{
internal static Func<R, S, T> Fun;
internal static T Call(R r, S s) => null != Fun ? Fun(r, s) : DefImpl<T>(r, s);
}
static GetValueImpl()
{
Specializer<int>.Fun = IntRet;
}
}
public class TestClass
{
//R and S are not specialized, we are specializing T
public T GetValue<R, S, T>(R r, S s) => GetValueImpl<R, S>.Specializer<T>.Call(r, s);
}
使用这种方式实现部分特化,你必须知道,如果你多次调用nested specializer class,helper class的静态构造函数将运行多次不同的非专业类型:
public void Test()
{
var foo = new TestClass();
//GetValueImpl<long, long> will be created at runtime
var v1 = foo.GetValue<long, long, int>(1, 2); //GetValueImpl<long, long>.Specializer<int> will be called, specialized
var v2 = foo.GetValue<long, long, string>(1, 2); //GetValueImpl<long, long>.Specializer<string> will be called
var v3 = foo.GetValue<long, long, long>(1, 2); //GetValueImpl<long, long>.Specializer<long> will be called
//GetValueImpl<long, int> will be created at runtime
var v4 = foo.GetValue<long, int, int>(1, 2); //GetValueImpl<long, int>.Specializer<int> will be called, specialized
var v5 = foo.GetValue<long, int, double>(1, 2); //GetValueImpl<long, int>.Specializer<double> will be called
}
注意GetValueImpl<long, long>
和GetValueImpl<long, int>
的静态构造函数会运行2次,所以不要在helperclass的静态构造函数里放额外的代码。
我知道 C# 中没有模板专业化,但这行得通(至少在编译时):
public T test0<T>()
{
return default(T);
}
// handling T=float case
public float test0()
{
return 0;
}
这甚至无法编译:
public T this[int i]
{
get { return default(T); }
set { }
}
public float this[int i]
{
get { return 0; }
set { }
}
也不是这个
public float this<float>[int i]
{
get { return 0; }
set { }
}
它说“.. 已经在浮动版本上定义了一个名为 'this' 的成员,其参数类型相同”。我可以检查通用 []
访问器中的所有类型,但是过多的 if-elseif
s 和 typeof()
s 会降低性能(我打算将其用作非托管数组而不是托管数组array) 和 dynamic
不是一个选项,因为我主要使用 .Net 3.5。在另一个问题中,我了解到我无法获取泛型类型的指针,因此我决定对每个浮点数、双精度数、整数和字节类型使用多个重载,并在此数组访问重载中使用这些重载(但不是在同一方法中但不同的不会失去性能。)
泛型 []
可以被某些原始类型重载,例如 float
吗?更具体地说,class 是否可以像下面的例子那样单独使用?
MyArr<float> arr = new MyArr<float>();
// specifically rounds to nearest for floats
arr[100]+=500f;
MyArr<int> arr = new MyArr<int>();
// specifically adds 1 to some performance counter variable
arr[100]+=500;
MyArr<byte> arr = new MyArr<byte>();
// does nothing special
arr[100]+=50;
如果答案是否定的,那么我将使用一个额外的 Interface
来实现此功能,但我不确定是否可以只为单个功能添加另一个接口到项目中。 (我应该在另一个问题中问 "is adding another interface just for a single method okay?" 吗?)
我认为以下内容可能会有所帮助:
public class GenericType<T>
{
public virtual T Test0()
{
return default(T);
}
public virtual T this[int i]
{
get { return default(T); }
set { }
}
}
public class FloatType : GenericType<float>
{
public override float Test0()
{
return 0;
}
public override float this[int i]
{
get { return 0; }
set { }
}
}
GenericType<float> nonOptimizedFloat = new GenericType<float>();
var defVal = nonOptimizedFloat[3]; // will use the non-optimized version
GenericType<float> optimizedFloat = new FloatType();
defVal = optimizedFloat[3]; // will use the optimized version
您可以拥有任意数量的优化类型,并且仍然在基础中保留一些通用逻辑 class。
您也可以考虑将基础 class 定义为抽象的,并确保始终使用优化版本。
您可以使用包含 nested generic class
和 Func/Action
委托的 helper class
来实现 Indexer/Property/Method.
internal static class IndexerImpl
{
private static T IndexerDefaultImpl<T>(int i) => default(T); //default implementation
private static T IndexerImpl2<T>(int i) => default(T); //another implementation for short/int/long
private static string IndexerForString(int i) => (i * i).ToString(); //specialization for T=string
private static DateTime IndexerForDateTime(int i) => new DateTime(i * i * i); //specialization for T=DateTime
static IndexerImpl() //install the specializations
{
Specializer<string>.Fun = IndexerForString;
Specializer<DateTime>.Fun = IndexerForDateTime;
Specializer<short>.Fun = IndexerImpl2<short>;
Specializer<int>.Fun = IndexerImpl2<int>;
Specializer<long>.Fun = IndexerImpl2<long>;
}
internal static class Specializer<T> //specialization dispatcher
{
internal static Func<int, T> Fun;
internal static T Call(int i)
=> null != Fun
? Fun(i)
: IndexerDefaultImpl<T>(i);
}
}
public class YourClass<T>
{
public T this[int i] => IndexerImpl.Specializer<T>.Call(i);
}
如果你需要YourClass
的实例来计算return值,你可以添加一个参数来传递需要的信息:
internal static class IndexerImpl
{
private static T IndexerDefaultImpl<T>(int i, YourClass<T> yourClass) => default(T); //default implementation
private static T IndexerImpl2<T>(int i, YourClass<T> yourClass) => default(T); //another implementation for short/int/long
private static string IndexerForString<T>(int i, YourClass<T> yourClass) => (i * i).ToString(); //specialization for T=string
private static DateTime IndexerForDateTime<T>(int i, YourClass<T> yourClass) => new DateTime(i * i * i); //specialization for T=DateTime
static IndexerImpl() //install the specializations
{
Specializer<string>.Fun = IndexerForString;
Specializer<DateTime>.Fun = IndexerForDateTime;
Specializer<short>.Fun = IndexerImpl2;
Specializer<int>.Fun = IndexerImpl2;
Specializer<long>.Fun = IndexerImpl2;
}
internal static class Specializer<T> //specialization dispatcher
{
internal static Func<int, YourClass<T>, T> Fun;
internal static T Call(int i, YourClass<T> yourClass)
=> null != Fun
? Fun(i, yourClass)
: IndexerDefaultImpl(i, yourClass);
}
}
public class YourClass<T>
{
public T this[int i] => IndexerImpl.Specializer<T>.Call(i, this);
}
在专门化通用索引器时,需要 非通用 助手 class。
当专门化非泛型 class 的泛型 Property/Method 时,default implementation
/specializations
和 nested specialization dispatcher class
可以直接放在你的 class.
你要注意的是,不要把特化安装到 generic class.
的静态构造函数中此外,如果您需要部分特化,您可以使用通用助手 class,其参数化类型不需要特化:
internal static class GetValueImpl<R, S>
{
private static T DefImpl<T>(R r, S s) => default(T);
private static int IntRet(R r, S s) => int.MaxValue;
internal static class Specializer<T>
{
internal static Func<R, S, T> Fun;
internal static T Call(R r, S s) => null != Fun ? Fun(r, s) : DefImpl<T>(r, s);
}
static GetValueImpl()
{
Specializer<int>.Fun = IntRet;
}
}
public class TestClass
{
//R and S are not specialized, we are specializing T
public T GetValue<R, S, T>(R r, S s) => GetValueImpl<R, S>.Specializer<T>.Call(r, s);
}
使用这种方式实现部分特化,你必须知道,如果你多次调用nested specializer class,helper class的静态构造函数将运行多次不同的非专业类型:
public void Test()
{
var foo = new TestClass();
//GetValueImpl<long, long> will be created at runtime
var v1 = foo.GetValue<long, long, int>(1, 2); //GetValueImpl<long, long>.Specializer<int> will be called, specialized
var v2 = foo.GetValue<long, long, string>(1, 2); //GetValueImpl<long, long>.Specializer<string> will be called
var v3 = foo.GetValue<long, long, long>(1, 2); //GetValueImpl<long, long>.Specializer<long> will be called
//GetValueImpl<long, int> will be created at runtime
var v4 = foo.GetValue<long, int, int>(1, 2); //GetValueImpl<long, int>.Specializer<int> will be called, specialized
var v5 = foo.GetValue<long, int, double>(1, 2); //GetValueImpl<long, int>.Specializer<double> will be called
}
注意GetValueImpl<long, long>
和GetValueImpl<long, int>
的静态构造函数会运行2次,所以不要在helperclass的静态构造函数里放额外的代码。