Reputation: 19966
Using C# 4.0. I want to convert an instance of MyBuffer<int>
to an instance of MyBuffer<float>
. The converter must be general enough to handle other basic types too.
I want this (or equivalent solution) to work. HOW?
var b1 = MyBuffer.Create(new int[100]);
var b2 = Convert.ChangeType(b1, typeof(MyBuffer<float>));
var b3 = Convert.ChangeType(b2, typeof(MyBuffer<byte>));
Consider the MyBuffer
class:
public class MyBuffer
{
public static MyBuffer<T> Create<T>(T[] buffer)
where T : struct, IComparable, IConvertible
{
return new MyBuffer<T>(buffer);
}
}
public class MyBuffer<T> : IConvertible
where T : struct, IComparable, IConvertible
{
public T[] Buffer
{
get;
set;
}
public MyBuffer()
{
}
public MyBuffer(T[] buffer)
{
Buffer = buffer;
}
public object ToType(Type conversionType, IFormatProvider provider)
{
if (conversionType == GetType())
return this;
// First problem: Determine if the type is MyBuffer<>.
// if (conversionType == typeof(MyBuffer<>))
{
if (conversionType.IsGenericType && conversionType.GetGenericArguments().Length > 0)
{
var bufferType = conversionType.GetGenericArguments()[0];
dynamic newBuffer = Buffer.
Select(s => Convert.ChangeType(s, bufferType))
.ToArray();
// Second problem: Our dynamic variable will produce an exception here.
return MyBuffer.Create(newBuffer);
}
}
throw new InvalidCastException();
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//
// For completeness...
//
public TypeCode GetTypeCode()
{
throw new NotImplementedException();
}
public bool ToBoolean(IFormatProvider provider)
{
throw new NotImplementedException();
}
public byte ToByte(IFormatProvider provider)
{
throw new NotImplementedException();
}
public char ToChar(IFormatProvider provider)
{
throw new NotImplementedException();
}
public DateTime ToDateTime(IFormatProvider provider)
{
throw new NotImplementedException();
}
public decimal ToDecimal(IFormatProvider provider)
{
throw new NotImplementedException();
}
public double ToDouble(IFormatProvider provider)
{
throw new NotImplementedException();
}
public short ToInt16(IFormatProvider provider)
{
throw new NotImplementedException();
}
public int ToInt32(IFormatProvider provider)
{
throw new NotImplementedException();
}
public long ToInt64(IFormatProvider provider)
{
throw new NotImplementedException();
}
public sbyte ToSByte(IFormatProvider provider)
{
throw new NotImplementedException();
}
public float ToSingle(IFormatProvider provider)
{
throw new NotImplementedException();
}
public string ToString(IFormatProvider provider)
{
throw new NotImplementedException();
}
public ushort ToUInt16(IFormatProvider provider)
{
throw new NotImplementedException();
}
public uint ToUInt32(IFormatProvider provider)
{
throw new NotImplementedException();
}
public ulong ToUInt64(IFormatProvider provider)
{
throw new NotImplementedException();
}
}
In my code above there are two major problems that need to be solved:
Type
variable equals SomeType<T>
for an arbitrary value of T
?T
set to some Type
variable?Upvotes: 0
Views: 162
Reputation: 3328
Just throwing this out there. There is a great library for converting object types called AutoMapper.
http://automapper.codeplex.com/
Upvotes: 1
Reputation: 437544
Since MyBuffer<T>
very helpfully includes a constructor that takes the buffer to wrap as an argument, you can simply convert one buffer to another manually (very easy with LINQ) and create a new "converted" instance:
var b1 = MyBuffer.Create(new int[100]);
var b2 = MyBuffer.Create(b1.Buffer.Select(i => (float)i).ToArray());
// another way to do the same:
var b3 = MyBuffer.Create(b1.Buffer.Select(Convert.ToSingle).ToArray());
Update:
In order to assuage Daniel's concern that I might have hidden intentions, here is how to do what the question asks with reflection, but of the more convenient form where the runtime does the digging in your place:
dynamic ConvertArray<T>(T[] input, Type target) {
var result = Array.CreateInstance(target, input.Length);
for (var i = 0; i < input.Length; ++i)
{
result.SetValue(Convert.ChangeType(input[i], target), i);
}
return result;
}
This method allows you to do this:
var ints = new[] { 1, 2, 3 };
var strings = ConvertArray(ints, typeof(string));
foreach (var s in strings) {
Console.WriteLine("[{0}] {1}", s.GetType(), s + " potato");
}
As is evident, strings
behaves exactly like an array of strings. Of course being dynamic
means that this particular array is never going to be able to mix with e.g. lambdas and the moral equivalent of reflection is still going on at runtime (only you don't see it). So it's not quite a free lunch, but it can prove useful at times.
Upvotes: 4
Reputation: 86610
To the first problem:
if (conversionType.GetGenericTypeDefinition() == typeof(MyBuffer<>))
To the second problem:
//your method....
//....
if (conversionType.IsGenericType && conversionType.GetGenericArguments().Length > 0)
{ //in fact you don't need this if in case the first problem is solved.
var bufferType = conversionType.GetGenericArguments()[0];
Func<MyBuffer<object>> AuxMethod = BufferConversion<object>;
return AuxMethod.Method.GetGenericMethodDefinition().MakeGenericMethod(bufferType).Invoke(this, null);
}
//....continue your method....
private MyBuffer<NewType> BufferConversion<NewType>()
{
NewType[] MyNewArray = Buffer.Select(s => (NewType)Convert.ChangeType(s, typeof(NewType))).ToArray();
return MyBuffer.Create(MyNewArray);
}
Upvotes: 1