通用索引器重载特化

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-elseifs 和 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 classFunc/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/specializationsnested 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的静态构造函数里放额外的代码。