如何从List <object>返回特定类型的对象

Noo*_*per 0 .net c# .net-3.5

我有一个 List<object>各种类型的对象的集合.

我正在编写一个辅助方法,它将返回一个特定类型的对象.辅助方法将接受类型名称作为字符串参数.

注意:我使用的是3.5框架.

Adr*_*tti 8

如果需要使用字符串作为参数,则不能依赖OfType<T>()扩展方法.幸运的是,它很容易模仿:

public IEnumerable<object> OfType(this List<object> list, string typeName)
{
    return list.Where(x => x != null && x.GetType().Name == typeName);
}
Run Code Online (Sandbox Code Playgroud)

正如@ChrisSinclair在评论中所指出的,该解决方案不管理转换,强制转换和继承/接口.转换(因为用户定义的转换运算符)和转换(因为TypeConverters和IConvertible接口)有点棘手.对于简单(隐式)强制转换(如继承和接口),您可以使用:

public IEnumerable<object> OfType(this List<object> list, string typeName)
{
    Type type = Type.GetType(typeName);
    return list.Where(x => x != null && type.IsAssignableFrom(x.GetType()));
}
Run Code Online (Sandbox Code Playgroud)

如何在运行时执行转换(即使使用CUSTOM CONVERSION OPERATORS)

我发现我需要的东西就像我在这个答案中发布的代码一样,但我不得不扩展它一点,这里是一个更好的实现,它负责自定义转换和转换.

将所有内容放在一个CastExtensions类中(如果不这样做,则更新代码)然后enum将其声明为小选项:

[Flags]
public enum CastOptions
{
    None = 0,
    ExcludeNulls = 1,
    UseConversions = 2
}
Run Code Online (Sandbox Code Playgroud)

问题是,C#,一般是静态类型语言,这意味着几乎所有(关于类型)必须在编译时是已知的(然后执行,你必须知道键入要投射到在编译时投).此函数处理简单情况(如派生)和更复杂的情况(接口,自定义转换运算符 - 强制转换 - 和转换 - 在需要时).

public static IEnumerable<object> OfType(this List<object> list, 
                                         string typeName, CastOptions options)
{
    Type type = Type.GetType(typeName);

    foreach (var obj in list)
    {
        if (Object.ReferenceEquals(obj, null))
        {
            if (options.HasFlag(CastOptions.ExcludeNulls))
                continue;

            yield return obj;
        }

        var objectType = obj.GetType();

        // Derived type?
        if (type.IsAssignableFrom(objectType))
            yield return obj;

        // Should we try to convert?
        if (!options.HasFlag(CastOptions.UseConversions))
            continue;

        // Castable?
        object convertedValue = null;

        try
        {
            var method = typeof(CastExtensions)
                .GetMethod("Cast", BindingFlags.Static|BindingFlags.NonPublic)
                .MakeGenericMethod(type);

            convertedValue = method.Invoke(null, new object[] { obj });
        }
        catch (InvalidCastException)
        {
            // No implicit/explicit conversion operators
        }

        if (convertedValue != null)
            yield return convertedValue;

        // Convertible?
        if (options.HasFlag(CastOptions.UseConversions))
        {
            try
            {
                IConvertible convertible = obj as IConvertible;
                if (convertible != null)
                    convertible.ToType(type, CultureInfo.CurrentCulture);
            }
            catch (Exception)
            {
                // Exact exception depends on the source object type
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,转换可能与转换等效或不等效,实际上它取决于实现以及操作中涉及的确切类型(这就是您可以通过选项启用或禁用此功能的原因).

这是在运行时转换所需的小辅助函数:

private static T Cast<T>(object obj)
{
    return (T)obj;
}
Run Code Online (Sandbox Code Playgroud)

我们可以在运行时发出这个代码(我想甚至使用表达式,但我没有尝试)但是一个小的帮助器方法将生成我们需要的代码(从一个对象转换为一个在运行时类型已知的通用).请注意,此如预期的值类型功能不起作用,例如:

int a = 1;
float a = Cast<float>(a); // Run-time error
Run Code Online (Sandbox Code Playgroud)

这是因为(object)1无法将其转换为其他任何内容int(对于所有盒装值类型都是如此).如果您使用的是C#4.0,则应将object参数更改为obj,dynamic并且所有内容都将按预期工作(适用于所有类型).

  • Anil没有指定,但这不会考虑继承/接口.如果那很好,那就更好了,否则会变得更复杂/慢.不要敲你的解决方案(+1),只是这样Anil知道. (2认同)