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

时间:2012-12-21 15:20:56

标签: c# .net .net-3.5

我有一个List<object>,它是各种类型对象的集合。

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

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

6 个答案:

答案 0 :(得分:8)

如果您需要使用字符串作为参数,则不能依赖OfType<T>()扩展方法。幸运的是,模拟

很容易。

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

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

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()));
}

如何在运行时执行转换(即使使用CUSTOM转换运算符)

我发现我需要的东西就像我在这个答案中发布的代码一样但是我必须稍微扩展一下,这里有一个更好的实现来处理自定义强制转换和转换。

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

[Flags]
public enum CastOptions
{
    None = 0,
    ExcludeNulls = 1,
    UseConversions = 2
}

问题是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
            }
        }
    }
}

请注意,转化可能与演员有效或不等同,实际上取决于 实施和操作涉及的确切类型(这就是你的原因 可以通过选项启用或禁用此功能

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

private static T Cast<T>(object obj)
{
    return (T)obj;
}

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

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

这是因为(object)1无法转换为int以外的任何内容(对于所有盒装值类型都是如此)。如果您使用的是C#4.0,则应将参数object的{​​{1}}更改为obj,一切都将按预期工作(适用于所有类型)。

答案 1 :(得分:2)

也许是这样的:

var ofTypeTypeA = myList.OfType<TypeA>();

答案 2 :(得分:0)

一种干净的方法是强制用户将类型指定为类型,以避免应用程序中出现松散的字符串。

然后你可以使用泛型并只使用你感兴趣的类型。这也可以让调用者在以后使用IEnumerable时跳过强制转换。

所以不要这样:

List<object> newList = GetOfType(myList, "SomeObject");
// CAST!!
SomeObject someObject = newList[0] as SomeObject;
if (someObject != null)
    // use object
你会这样做:

IEnumerable<SomeObject> newList = myList.OfType<SomeObject>();
foreach (SomeObject someObject in newList){
    // no cast neccessary

如果你应该重命名类SomeObject,这将使它在将来变得不敏感(因为重构工具会选择类名而不是字符串)

答案 3 :(得分:-1)

您可以使用Enumerable.OfType

var input = new List<object>();
input.Add(1);
input.Add("foo");
var bar = input.OfType<string>();

答案 4 :(得分:-1)

我猜你需要将从列表中提取的单个对象强制转换为强类型对象。而不是把所有的名单都列入其中。否则使用List<MyType>

所以我会这样做:How to cast to a type in C#

答案 5 :(得分:-3)

您可以使用is运算符(或传递类型并使用is检查)。以下是使用is运算符的示例:

foreach (var ctl in ControlsList)
{
    if (ctl is CheckBox)
        //Do this
    else if (ctl is TextBox)
        //DoThis
}

通过在参数中将类型作为字符串传递,您可以执行类似的操作来获取要测试的类型:

Type t = System.Type.GetType("System.Int32");