Reputation: 957
It seems we cannot call type conversion operator easily in C# generic class. Here is the code. Why?
T006 finally archive our target.
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Reflection;
using System.Linq;
namespace ConsoleApplication1
{
class vec<T, T2> : List<T> where T : class
{
public vec(IEnumerable<T2> other)
{
//Converter<T2, T> cvt = (v) => (T)v; // T004 failed, try defined function dynamicly, cannot compile, too.
// T006 pass, client happy, we not happy, but anyway passed, performance may not happy.
var conversionOperator = typeof(T).GetMethods(BindingFlags.Static | BindingFlags.Public)
.Where(m => m.Name == "op_Explicit" || m.Name == "op_Implicit")
.Where(m => m.ReturnType == typeof(T))
.Where(m => m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(T2))
.FirstOrDefault();
Func<T2, T> cvt = (obj) =>
{
if (conversionOperator != null)
return (T)conversionOperator.Invoke(null, new object[] { obj });
else
return default(T);
};
foreach (T2 item in other)
{
//Add((T)item); // T001 failed, this line cannot compile
//Add(item as T); // T002 failed, this line alwasy return null. // http://msdn.microsoft.com/en-us/library/vstudio/cscsdfbt.aspx
//Add((T)(object)item); // T003 failed, pass compile, but throw exception at runtime.
Add(cvt(item)); // T006 pass.
}
}
// T005 pass, but clients for this code will not happy.
public vec(Converter<T2, T> cvt, IEnumerable<T2> other)
{
foreach (T2 item in other)
{
Add(cvt(item));
}
}
}
class XXX
{
public int foo = 22;
static public explicit operator XXX(YYY other)
{
XXX me = new XXX();
me.foo = (int)other.foo;
return me;
}
}
class YYY
{
public float foo = 11;
}
class Program
{
static void Main(string[] args)
{
YYY[] from = new YYY[2];
for (int i = 0; i < from.Length; i++)
{
from[i] = new YYY();
}
XXX x = (XXX)from[0];
vec<XXX, YYY> coll = new vec<XXX, YYY>(from);
// Not happy, this requires user have strong C# skill;
//vec<XXX, YYY> coll = new vec<XXX, YYY>((v) => (XXX)v, from);
foreach (var item in coll)
{
Debug.Print("Value is {0}", item.foo);
}
}
}
}
The compiler error for T001 is: Cannot convert type 'T2' to 'T'
Upvotes: 1
Views: 4380
Reputation: 23208
If you need to have your custom implicit/explicit conversion operators run, then casting to/from object
(or performing an as
cast) will skip them because the information is only known at compile-time when you are performing a runtime cast.
The only way I know of, through a general-use generic method such as you posted, is to leverage dynamic
which will lookup at runtime to see if there are any conversion operators defined and call them:
return (T2)(dynamic)obj;
Quick example:
public class Class1
{
public static implicit operator Class1(Class2 class2)
{
Console.WriteLine("implicit conversion from Class2 to Class1");
return new Class1();
}
public static implicit operator Class2(Class1 class1)
{
Console.WriteLine("implicit conversion from Class1 to Class2");
return new Class2();
}
}
public class Class2
{
}
public static T2 Convert<T1, T2>(T1 obj)
{
return (T2)(dynamic)obj;
}
var class1 = new Class1();
var class2 = Convert<Class1, Class2>(class1);
//outputs: implicit conversion from Class1 to Class2
Be aware though, that this using reflection and doing significant work at runtime, so thoroughly test and make sure performance is still acceptable.
EDIT: Since you do not have access to the dynamic language runtime, you can write your own conversion operator lookups with reflection:
public static T2 Convert<T1, T2>(T1 obj)
{
var conversionOperator = typeof(T1).GetMethods(BindingFlags.Static | BindingFlags.Public)
.Where(m => m.Name == "op_Explicit" || m.Name == "op_Implicit")
.Where(m => m.ReturnType == typeof(T2))
.Where(m => m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(T1))
.FirstOrDefault();
if (conversionOperator != null)
return (T2)conversionOperator.Invoke(null, new object[]{obj});
throw new Exception("No conversion operator found");
}
You may need to tweak the code (perhaps to attempt traditional casting if no operators are found), and I'm not sure if I can guarantee that this will work every time. I don't know if there are corner cases or platform quirks to handle. Not to mention that this will be pretty slow with the reflection. You could introduce a quick caching mechanism where you do an O(1) lookup with a Dictionary or something where you can store each conversion operator as they're found for each type combination.
Upvotes: 12
Reputation: 16286
You can first cast it to object
then T
:
Add((T)(object)item)
But you should be cautious about run-time errors, and define T
and T2
in a way that it does not cause an issue.
Upvotes: 2