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?)
source to share
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.
source to share
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.
source to share