General Indexer Overloading Specification

I know there is no specialized specialization in C #, but this works (at least at compile time):

    public T test0<T>()
    {
        return default(T);
    }

    // handling T=float case
    public float test0()
    {
        return 0;
    }

      

and this doesn't even compile:

    public T this[int i]
    {
        get { return default(T); }
        set { }
    }

    public float this[int i]
    {
        get { return 0; }
        set { }
    }

      

and

    public float this<float>[int i]
    {
        get { return 0; }
        set { }
    }

      

it says ".. already defines a member named 'w650' with the same parameter types" in the floating point version. I could check all types in a generic accessory []

, but too much if-elseif

and typeof()

would hurt performance (I will use this as an unmanaged array instead of a managed array) and dynamic

not an option as I use .Net 3.5 mostly. In another question, I found out that I cannot get a pointer to a generic type, so I decided to use multiple overloads for each float, double, int and byte type and use those overloads in that array access overload (but not in the same method but different to avoid losing performance.)

Can generic []

be overloaded with some primitive types like float

? More specifically, can a class only use itself like below, ever?

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;

      

if the answer is no, then I'm going to use an additional one Interface

to implement this functionality, but I'm not sure if I would still add another interface to the project for just one function. (should I ask "is adding another interface for only one method okay?" in another question?)

+3


source to share


2 answers


I think the following might be helpful:

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

      



You can have as many optimized types as you would like and still contain some common logic in your base class.

You can also consider defining the base class as abstract and ensure that the optimized versions are always used.

+1


source


You can use helper class

that contains a delegate nested generic class

to Func/Action

for the realization of specialization 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);
}

      

If you need an instance YourClass

to compute the return value, you can add a parameter to pass the required information:

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

      

When specializing a generic indexer, a non-generic helper class is required .

When assigning a generic property / method to a non-generic class, default implementation

/ specializations

and nested specialization dispatcher class

can be placed directly in your class.

What you should note, do not put the setting of specializations in the static constructor of the generic class .




Also, if you need partial specialization, you can use a generic helper class with parameterized types that don't need specialization:

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

      

When using this method to implement partial specialization, you should be aware that the static constructor of the helper class will execute multiple times if you call the nested specializer class multiple times with different non-specific types:

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
}

      

Note that the static constructor will run 2 times for GetValueImpl<long, long>

and GetValueImpl<long, int>

, so do not add extra code to the static constructor of the helper class.

+1


source







All Articles