我有一个名为Transfer
的泛型方法,它也返回一个泛型类型。在Transfer
的主体中,我进行了一些处理,得到了MessageBundle
对象。如何以通用方式重建Transfer
的返回类型(使用MessageBundle
对象(?
我只能让它以非通用的方式工作。
namespace ConsoleApp
{
internal class Program
{
static void Main(string[] args)
{
//this works
var test = Transfer<(string? Result, string? Error)>();
//???
var test = Transfer<(int? Result, string? Error)>();
var test = Transfer<(bool? Result, string? Error)>();
var test = Transfer<(object? Result, string? Error)>();
}
public static U Transfer<U>()
{
//getting MessageBundle object
var bundle = new MessageBundle
{
Result = "hi",
Error = null
};
//How to reconstruct in generic way?
(string? Result, string? Error) response = (bundle.Result as string, bundle.Error);
return (U)Convert.ChangeType(response, typeof(U));
}
}
public class MessageBundle
{
public string? Error { get; set; }
public object? Result { get; set; }
}
}
编辑1:我无法控制Transfer
方法的签名。
编辑2:U
(Transfer
方法(的形状将是两个元素的元组(Error
始终是字符串,而Result
可以变化(
如果您事先知道U
将是一个由2项组成的元组,但无法以任何方式更改public static U Transfer<U>()
的签名(即使添加了ITuple
约束(,则需要使用反射来构造元组。假设第一个元组参数是某种类型的.NET基元,并且Result
的值是其字符串表示,则可以使用Convert.ChangeType()
将字符串值转换为最终值。
一种方法是:
if (!typeof(ITuple).IsAssignableFrom(typeof(U)) // Assert it's a tuple
|| !(typeof(U).IsGenericType && typeof(U).GetGenericArguments() is var arguments && arguments.Length == 2) // Assert it has exactly two generic parameters
|| arguments[1] != typeof(string)) // Assert that the second parameter is a string
throw new ArgumentException(string.Format("Unexpected type {0}", typeof(U)));
var tuple = (U)Activator.CreateInstance(typeof(U),
bundle.Result == null ? null : Convert.ChangeType(bundle.Result, Nullable.GetUnderlyingType(arguments[0]) ?? arguments[0]),
bundle.Error)!;
注:
您没有指定在
Result
转换失败的情况下应该发生什么,例如,您为Result
指定了一个整数类型,但值是"hi"
。如果发生这种情况,上面的实现将从Convert.ChangeType()
中抛出一个异常。您也没有指定当
Result
对应于某个未实现IConvertible
的复杂对象时应该发生什么。在这种情况下,上面的实现也会抛出异常。Convert.ChangeType(object, Type)
使用当前区域性进行转换。如果您想使用不变区域性(例如,因为您正在反序列化通过连线接收的值(,请使用ChangeType(Object, Type, CultureInfo.InvariantCulture)
:var tuple = (U)Activator.CreateInstance(typeof(U), bundle.Result == null ? null : Convert.ChangeType(bundle.Result, Nullable.GetUnderlyingType(arguments[0]) ?? arguments[0], CultureInfo.InvariantCulture), bundle.Error)!;
你真的应该重新考虑这个设计。以这种方式使用反射几乎消除了所有编译时对代码正确性的检查。
在这里演示小提琴。