使用反射在两个通用列表之间转换

时间:2015-04-04 20:35:11

标签: c# type-conversion

是否有办法缩短以下方法(在扩展枚举方面更安全)?

private object MakeTypedList(IReadOnlyList<IData> readOnlyList, DataTypes dataTypes)
{
    switch (dataTypes)
    {
        case DataTypes.Byte:
            return readOnlyList
                .Cast<IByteData>();
        case DataTypes.Integer:
            return readOnlyList
                .Cast<IIntegerData>();
        case DataTypes.Float:
            return readOnlyList
                .Cast<IFloatData>();
        case DataTypes.Boolean:
            return readOnlyList
                .Cast<IBooleanData>();
        case DataTypes.String:
            return readOnlyList
                .Cast<IStringData>();
        case DataTypes.ByteArray:
            return readOnlyList
                .Cast<IByteArray>();
        case DataTypes.IntegerArray:
            return readOnlyList
                .Cast<IIntegerArray>();
        case DataTypes.FloatArray:
            return readOnlyList
                .Cast<IFloatArray>();
        case DataTypes.BooleanArray:
            return readOnlyList
                .Cast<IBooleanArray>();
        case DataTypes.StringArray:
            return readOnlyList
                .Cast<IStringArray>();
        case DataTypes.List:
            return readOnlyList
                .Cast<IListData>();
        case DataTypes.Image24:
            return readOnlyList
                .Cast<IImage24>();
        case DataTypes.Image8:
            return readOnlyList
                .Cast<IImage8>();
        case DataTypes.FloatSignal:
            return readOnlyList
                .Cast<IFloatSignal>();
        case DataTypes.IntegerSignal:
            return readOnlyList
                .Cast<IIntegerSignal>();
        case DataTypes.Record:
            return readOnlyList
                .Cast<IRecord>();
        case DataTypes.Raw:
            return readOnlyList
                .Cast<IRaw>();
        default:
            throw new InvalidEnumArgumentException("Unsupported DataType!");
    }
}

注意:标题必须保持不变,因为这些是我可以为此方法提供的数据。这是许多复杂架构的结果,无法更改。但是,我可以操作 - 例如 - 枚举本身或接口(例如添加属性等)。


提出同一问题的另一种方式。如果我有一个包含代表List<Base>类型的对象的List<Derived>变量,是否可以将Type投射到Derived

List<Base> b;
Type t = typeof(Derived)
object derivedList = // use t only to construct List<Derived>

1 个答案:

答案 0 :(得分:0)

反思非常难看,这肯定不比你已经写过的更短或更高效,但它会使它更具可扩展性。

首先,定义一个属性:

[AttributeUsage(AttributeTargets.Interface)]
public class DataTypeAttribute : Attribute
{
    public DataTypeAttribute(DataTypes dataType)
    {
        DataType = dataType;
    }

    public DataTypes DataType { get; private set; }
}

然后用它标记你的界面:

[DataType(DataTypes.Byte)]
public interface IByteData : IData
{
}

[DataType(DataTypes.Integer)]
public interface IIntegerData : IData
{
}

// etc...

请注意,我假设所有单个数据类型都继承自IData类型。 (如果不是,他们可能应该这样做。)

现在,您可以按如下方式实施MakeTypedList方法:

private object MakeTypedList(IReadOnlyList<IData> readOnlyList, DataTypes dataTypes)
{
    var type = typeof(IData).Assembly.GetTypes()
        .Where(t => t.IsInterface && typeof (IData).IsAssignableFrom(t))
        .FirstOrDefault(t =>
        {
            var attribute = t.GetCustomAttributes(typeof(DataTypeAttribute), false)
                .FirstOrDefault() as DataTypeAttribute;
            return attribute != null && attribute.DataType == dataTypes;
        });

    if (type == null)
        throw new InvalidEnumArgumentException("Unsupported DataType!");

    var enumerable = typeof(Enumerable)
        .GetMethod("OfType", BindingFlags.Static | BindingFlags.Public)
        .MakeGenericMethod(type)
        .Invoke(null, new object[] {readOnlyList});

    // you can omit this last step if you just want an IEnumerable<T>
    var list = typeof(Enumerable)
        .GetMethod("ToList", BindingFlags.Static | BindingFlags.Public)
        .MakeGenericMethod(type)
        .Invoke(null, new object[] { enumerable });

    return list;
}

参见 - 丑陋。

请注意,我使用的是OfType而不是Cast。这会将列表过滤为所需类型,而不是在遇到不属于所请求类型的项目时抛出异常。如果您确实希望抛出异常,只需将"OfType"更改回"Cast"

另请注意,为此,所有数据类型接口必须与IData接口位于同一个程序集中。

关于您提供的第二个示例,您可以编写类似的扩展方法:

public static object Cast<TBase>(this IEnumerable<TBase> original, Type type)
{
    return typeof(Enumerable)
        .GetMethod("Cast", BindingFlags.Static | BindingFlags.Public)
        .MakeGenericMethod(type)
        .Invoke(null, new object[] { original });
}

这样可以很容易地使用您提供的变量,如下所示:

List<Base> b;
Type t = typeof(Derived);

object derivedList = b.Cast(t);