假设我有一个用于多态的抽象基类,我喜欢编写一个方法将一个派生类型转换为另一个派生类型,但我不知道编译时的任何派生类型。我相信反思是解决这个问题的正确方法,但我不确定实现它的正确方法是什么。这就是我目前所处的位置。
Public Static BaseClass ConvertTo(BaseClass bc, Type type) {
//bc is currently DerivedClass1 which implements IConvertable
//type is currently DerivedClass2
//Trying to convert DerivedClass1 to DerivedClass2
return (BaseClass)ChangeType(bc, type);
}
此方法将DerivedClass1转换为DerivedClass2,但我必须为DerivedClass1实现IConvertable接口才能工作,我不满意因为它带有15个不必要的方法我必须实现。
是否有一种更优雅的方法来解决这个问题,这是一种更接近编译时间的东西?取决于DerivedClass1是否具有DerivedClass2 Cast操作符,该方法将成功或抛出运行时异常的方法。更像是:
Public Static BaseClass ConvertTo(BaseClass bc, Type type) {
//First down cast it to bc, then sideways cast it to type.
return (type)((bc.GetType())bc)
}
答案 0 :(得分:3)
听起来您要求对用户定义的转换运算符进行反思。您可以通过询问公共静态方法并过滤到名为op_explicit
或op_implicit
的方法,使用正确的参数和返回类型,并包括MethodAttributes.SpecialName
来获得具有反思的人。然后正常调用该方法。
这里有一些示例代码我已经快速启动了 - 您可能希望添加更多的健壮性检查,包括对派生类型或基类型的转换等等......但它'开始:
using System;
using System.Linq;
using System.Reflection;
class Foo
{
public int Value { get; set; }
public static explicit operator Bar(Foo x) => new Bar { Value = x.Value };
public static explicit operator Foo(Bar x) => new Foo { Value = x.Value };
}
class Bar
{
public int Value { get; set; }
}
class Test
{
static void Main()
{
var foo = new Foo { Value = 10 };
var bar = Convert<Bar>(foo);
Console.WriteLine(bar.Value);
foo = Convert<Foo>(bar);
Console.WriteLine(foo.Value);
}
public static T Convert<T>(object source)
{
var conversion = FindConversion(source.GetType(), typeof(T));
if (conversion == null)
{
throw new InvalidOperationException("No conversion found");
}
return (T) conversion.Invoke(null, new[] { source });
}
private static MethodInfo FindConversion(Type fromType, Type toType)
{
var expectedParameterTypes = new[] { fromType };
var methods = from type in new[] { fromType, toType }
from method in type.GetMethods(BindingFlags.Public | BindingFlags.Static)
where method.Name == "op_Explicit" || method.Name == "op_Implicit"
where (method.Attributes & MethodAttributes.SpecialName) != 0
where method.ReturnType == toType
where method.GetParameters()
.Select(p => p.ParameterType)
.SequenceEqual(expectedParameterTypes)
select method;
return methods.FirstOrDefault();
}
}