l33t
l33t

Reputation: 19966

Convert MyClass<SomeType> to MyClass<SomeOtherType>

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:

  1. How do you determine if a Type variable equals SomeType<T> for an arbitrary value of T?
  2. Is it possible to call a templated function with T set to some Type variable?

Upvotes: 0

Views: 162

Answers (3)

Cubicle.Jockey
Cubicle.Jockey

Reputation: 3328

Just throwing this out there. There is a great library for converting object types called AutoMapper.

http://automapper.codeplex.com/

Upvotes: 1

Jon
Jon

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

Daniel M&#246;ller
Daniel M&#246;ller

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

Related Questions