Reputation: 7579
How do you cast a variable to a generic at runtime, when we have the generic argument as a variable of type "Type":
Here's a little test I knocked up that I'm trying to resolve. It's a contrived example, but illustrates my problem.
using System;
using System.Linq;
using NUnit.Framework;
namespace MyApp.Tests
{
public interface IMagicConverter<T>
{
T ConvertIt(int input);
}
/// <summary>
/// Convert and int to a somewhat related string
/// </summary>
public class MyStringConverter : IMagicConverter<string>
{
public string ConvertIt(int input)
{
switch (input)
{
case 1:
return "Number one";
case 2:
return "Number two";
default:
return "Another number";
}
}
}
/// <summary>
/// Convert a int to a somewhat related date
/// </summary>
public class MyDateTimeConverter : IMagicConverter<DateTime>
{
public DateTime ConvertIt(int input)
{
return DateTime.Today.AddDays(input);
}
}
public class TestClass
{
// at this point we know that myConverter implements a IMagicConverter
public void TestMethod(Type myConverter, int number, ref object returnvalue)
{
// work out what the generic argument is
var typeArgument = myConverter.GetInterfaces()
.Where(x => x.IsGenericType)
.Where(x => x.GetGenericTypeDefinition() == typeof (IMagicConverter<>))
.Select(x => x.GetGenericArguments())
.First();
// cast it to that type
var instance = (IMagicConverter<typeArgument>)Activator.CreateInstance(myConverter);
returnvalue = instance.ConvertIt(number);
}
}
[TestFixture]
public class MyTest
{
[Test]
public void MyStringConverter_ConvertsTheNumberOne_ToEnglish()
{
// Arrange
var test = new TestClass();
object returnValue = null;
// Act
test.TestMethod(typeof(MyStringConverter), 1, ref returnValue);
// Assert
Assert.That(returnValue, Is.EqualTo("Number one"));
}
}
Upvotes: 2
Views: 96
Reputation: 1064184
Basically, you need to use reflection every step - you'd need to find the method you want via reflection, and invoke that with reflection. Hugely ugly.
At that point, it is tempting to cheat:
public void TestMethod(Type myConverter, int number, ref object returnvalue)
{
dynamic obj = Activator.CreateInstance(myConverter);
returnvalue = Evil(obj, number);
}
static T Evil<T>(IMagicConverter<T> converter, int input)
{
return converter.ConvertIt(input);
}
The trick here is the dynamic
, which lets the DLR worry about all of that, with the advantage that it has inbuilt per-type strategy caching, so you only pay any reflection cost once.
The other common approach is to have a non-generic interface, for example:
public interface IMagicConverter
{
object ConvertIt(int input);
}
public interface IMagicConverter<T> : IMagicConverter
{
new T ConvertIt(int input);
}
Then you use:
var obj = (IMagicConverter)Activator.CreateInstance(myConverter);
returnvalue = obj.ConvertIt(number);
Here, to satisfy the interfaces, you would also need to add:
object IMagicConverter.ConvertIt(int input)
{
return ConvertIt(input);
}
to both MyStringConverter
and MyDateTimeConverter
, to forward the non-generic call to the generic call.
Upvotes: 1