通用索引器过载专业化

时间:2017-03-22 12:40:05

标签: c# generics template-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 { }
    }

它说&#34; ..已经定义了一个名为&#39; this&#39;使用相同的参数类型&#34;在浮动版本上。我可以检查通用[]访问器中的所有类型,但是太多if-elseiftypeof()会降低性能(我将把它用作非托管数组而不是托管数组)和dynamic不是一个选项,因为我主要使用.Net 3.5。在另一个问题中,我已经知道我无法获得泛型类型的指针,因此我决定每个float,double,int和byte类型使用多个重载,并在此数组访问重载中使用这些重载(但是不是同一种方法,而是不同的方法,不会失去性能。)

某些基本类型(如[])是否可以重载通用float?更具体地说,一个类本身可以像以下一样使用吗?

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来实现此功能,但我不确定是否可以将另一个界面添加到项目中单个功能。 (我应该问&#34;是否只为一种方法添加另一个界面好吗?&#34;在另一个问题中?)

2 个答案:

答案 0 :(得分:1)

我认为以下内容可能会有所帮助:

main()

您可以拥有任意数量的优化类型,并在基类中保留一些常用逻辑。

您还可以考虑将基类定义为抽象,并确保始终使用优化版本。

答案 1 :(得分:1)

您可以使用包含helper classnested generic class委托的Func/Action来实现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的实例来计算返回值,您可以添加一个参数来传递所需的信息:

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);
}

在专门设计通用索引器时,需要非泛型辅助类。

在专门化非泛型类的通用属性/方法时,default implementation / specializationsnested specialization dispatcher 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);
}

使用这种方式实现部分特化,你必须意识到,如果用不同的非专用类型多次调用嵌套的专业化类,辅助类的静态构造函数将运行多次:

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次,因此不要在辅助类的静态构造函数中添加额外的代码。