【问题标题】:How to cast a object to IList<T> with unknown type T如何将对象转换为未知类型 T 的 IList<T>
【发布时间】:2011-04-24 12:38:12
【问题描述】:


我有一个 object[] 包含一些值。我想从中提取信息,但无法将数组中的第二个对象(WeakReference)转换为 IList,其中 T 作为数组中的第三个值。

看看我的代码:

object[] vals = GetValues(); //vals[2] = WeakReference, vals[3] = Type of T, vals[4] = index in the list
IList<((Type)vals[3])> list = (IList<((Type)vals[3])>)(((WeakReference)vals[2]).Target); //This line does not even compile, seems like I'm doing something wrong..
object oo = list.ElementAt((int)vals[4]);
//Do something with oo...

有什么建议可以将 WeakReference 的目标转换为 T = vals[3] 的 IList 接口吗?

【问题讨论】:

  • 你为什么不直接转换成IList
  • 因为我需要 ElementAt 方法,它只适用于 IList
  • IList有Item属性,可以list[(int)vals[4]]

标签: c# .net windows generics casting


【解决方案1】:

您将这么多异构信息打包到一个数组中,这真的很奇怪。数组通常用于存储 same 类型的元素。为什么不把数据封装成合适的类型呢?

但要按要求回答问题 - 在 C# 4 中,您可以使用 dynamic:

var target = ((dynamic)vals[2]).Target;

if(target != null)
{
    object oo = Enumerable.ElementAt(target, vals[4]);
    //Do something with oo...
}

(编辑:如果您想在此处最小化dynamic 的使用,请转换为WeakReference 并将动态调用留到最后。这样,类型安全性就“最大化”了。)

否则,你可以使用反射:

object target = ((WeakReference)vals[2]).Target;

if (target != null)
{
    object oo = target.GetType()
                      .GetProperty("Item")
                      .GetValue(target, new[] { vals[4] });
    //Do something with oo...
}

(编辑:如果可以显式实现索引器,您可能需要使用接口映射。)

【讨论】:

  • 如果您知道对象是WeakReference,那么转换为dynamicWeakReference 有什么优势?
  • 我试过这个,但如果属性是显式实现的,因此是私有的,.GetProperty("Item") 返回null
  • @tvanfosson:当然。不过稍后将需要另一个 dynamic 演员表。
  • 这个解决方案对我来说真的有点矫枉过正。为什么需要 C# 4 的“动态”或反射来在可枚举中的给定位置获取对象?
  • @Simon:请记住,在这种情况下,IEnumerableIList&lt;T&gt;。通过不使用索引器,我们使操作 O(n) 而不是 O(1)。当然,如果您扩展您的解决方案以尝试查找索引器,并在不存在的情况下使用枚举,那就没问题了。它将是ElementAt 的更通用版本,适用于任何(非通用)IEnumerable。在这种情况下,我只是选择了更简单的解决方案。
【解决方案2】:

已经有人建议您可以使用dynamic,但听起来您还应该检查对象是否具有指定的类型。我也尽量少用dynamic

object target = ((WeakReference) vals[2]).Target;

if (target == null)
    throw new InvalidOperationException("Target cannot be null.");

object result = Enumerable.ElementAt((dynamic) target, (int) vals[4]);

if (result != null && !((Type) vals[3]).IsAssignableFrom(result.GetType()))
    throw new InvalidOperationException(string.Format("The retrieved object is a {0}, but the expected type was {1}.", result.GetType(), (Type) vals[3]));

return result;

【讨论】:

    【解决方案3】:

    如果您只需要给定索引处的可枚举对象,这里有一个简单的函数:

        public static object GetObjectAt(IEnumerable enumerable, int index)
        {
            int i = 0;
            foreach (object obj in enumerable)
            {
                if (i == index)
                    return obj;
    
                i++;
            }
            throw new IndexOutOfRangeException();
        }
    

    在你的情况下,你会这样做:

            object oo = GetObjectAt((IEnumerable)(WeakReference)vals[2], (int)vals[4]);
    

    当然,还有一些看起来更性感的替代品(请参阅其他答案),带有花哨的 linq 查询和酷炫的 C# 4 动态新时尚东西 :-) 但是,一般来说,如果你不“需要” T 类型(在您的示例代码中,您不需要它),您不需要泛型。任何 .NET Framework 和 C# 版本(从 1 到 4)实际上都支持此解决方案。

    【讨论】:

    • 哈哈对我来说最好的答案西蒙你让我微笑。对于任何想了解“老派”执行方式的人来说,这是一个很好的起点,然后您可以随时使用 Linq/Lambda 等在此基础上进行构建
    【解决方案4】:

    我认为您工作太努力了,无法使用似乎没有必要的扩展方法。

    object[] vals = GetValues();
    var list = ((WeakReference)vals[2]).Target as IList;
    object oo = null;
    if (list != null)
    {
        oo = list[(int)vals[4]];
    }
    

    【讨论】:

    • IList&lt;T&gt;not 实现IList
    • @Ani - 但List&lt;T&gt; 确实实现了IList,因此无论目标是否是强类型列表,它都应该工作。
    • 当然,但 OP 并没有说 IList&lt;T&gt; 将是 List&lt;T&gt;。顺便说一句,我不是反对者。
    • @Timwi - 所以你建议他创建了一个实现IList&lt;T&gt; 但不是IList 的特殊类,因为他试图转换为IList&lt;T&gt; 而不是使用通用的内置列出类?对我来说似乎有点牵强,但我认为这是可能的。引用文档:The IList(T) generic interface is a descendant of the ICollection(T) generic interface and is the base interface of all generic lists.AFAIK,所有实现IList&lt;T&gt; 的通用列表也实现IList
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-05-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多