【问题标题】:How can I detect if an object is an ILookup<,> and print it?如何检测对象是否是 Lookup<,> 并打印它?
【发布时间】:2011-02-23 14:15:33
【问题描述】:

我正在尝试制作一个用于调试的非常基本的通用对象打印机,灵感来自于 LinqPad 的出色表现。

以下是我的打印功能的伪代码。我的反射-foo 目前有点弱,我正在努力处理对象是 ILookup 的情况,因为我想枚举查找,将每个键与其关联的集合一起打印。

ILookup 没有非通用接口,也没有实现 IDictionary,所以我现在有点卡住了,因为我不能说 o as ILookup&lt;object,object&gt;... 就此而言,我想知道如何深入研究任何通用接口...假设我想为CustomObject&lt;,,&gt; 提供一个特例。

void Print(object o)
{
    if(o == null || o.GetType().IsValueType || o is string)
    {
         Console.WriteLine(o ?? "*nil*");
         return;
    }

    var dict = o as IDictionary;     
    if(dict != null)
    {
        foreach(var key in (o as IDictionary).Keys)
        {
            var value = dict[key];
            Print(key + " " + value);
        }
        return;
    }

    //how can i make it work with an ILookup? 
    //????????? 


    var coll = o as IEnumerable;
    if(coll != null)
    {
        foreach(var item in coll)
        { print(item); }
        return;
    }

    //else it's some object, reflect the properties+values
    {
        //reflectiony stuff
    }
}

【问题讨论】:

  • 这也可能更容易使用多态性,即void Print(IDictionary dict)void Print(IEnumerable ienum)void Print(object o) 等。
  • @mellamokb - 我也这么认为。也许我做错了,但相互递归的 Print 调用的行为与您预期的不同。
  • 根据@xanatos,ILookupIEnumerableIEnumerable。看起来您当前的代码应该按原样工作。如果传入ILookup 类型的对象会发生什么?
  • @Kobi:我发现我还必须添加 void Print(string s) 才能正常工作。
  • 作为说明,我删除了我的回复,因为我“发现”对象是否为 ILookup 的方式是错误的(我只检查了 Lookup 实现)。 IEnumerable 的 IEnumerable 是真的并且可以工作,但他会“丢失”“关键”部分。 IEnumerable的IEnumerable只会给他值部分。

标签: c# reflection


【解决方案1】:

我不确定您到底要完成什么,但要回答您的具体问题,您可以像这样使用反射:

public static void PrintIfLookup(object obj)
{
    if (obj == null)
        throw new ArgumentNullException("obj");

    // Find first implemented interface that is a constructed version of
    // ILookup<,>, or null if no such interface exists.
    var lookupType = obj
                    .GetType()
                    .GetInterfaces()
                    .FirstOrDefault
                     (i => i.IsGenericType &&
                           i.GetGenericTypeDefinition() == typeof(ILookup<,>));

    if (lookupType != null)
    {
        // It is an ILookup<,>. Invoke the PrintLookup method
        // with the correct type-arguments.

        // Method to invoke is private and static.
        var flags = BindingFlags.NonPublic | BindingFlags.Static;

        // Assuming the containing type is called Foo.
        typeof(Foo).GetMethod("PrintLookup", flags)
                   .MakeGenericMethod(lookupType.GetGenericArguments())
                   .Invoke(null, new[] { obj });
    }

}

private static void PrintLookup<TKey, TElement>(ILookup<TKey, TElement> lookup)
{
    // TODO: Printing logic    
}

我尝试以这样一种方式编写它,即您可以使用泛型以强类型的方式编写打印逻辑。如果您愿意,您可以改为进行更多反射以从查找中的每个 IGrouping&lt;,&gt; 中获取键和值。

编辑:顺便说一句,如果您使用的是 C# 4,则可以将 if 语句的整个主体替换为:

PrintLookup((dynamic)obj);

【讨论】:

    【解决方案2】:

    多态性可能会使您的代码更简单。

    void Print(IDictionary dict)
    {
        foreach (var key in dict.Keys)
        {
            var value = dict[key];
            Print(key + " " + value);
        }
    }
    
    void Print(object o)
    {
        if (o == null || o.GetType().IsValueType || o is string)
        {
            Console.WriteLine(o ?? "*nil*");
            return;
        }
    }
    
    void Print(string s)
    {
        Console.WriteLine(s);
    }
    
    void Print(IEnumerable ie)
    {
        foreach (dynamic obj in ie)
        {
            Print(obj);
        }
    }
    

    【讨论】:

    • 这也是我的问题,你需要使用dynamic,老实说,我想避免。程序集,您希望在 IDictionary 键/值、ILookup 等上使用动态格式。
    • @Kobi:dynamic 和反射真的有区别吗?它们都将类型检查移至运行时,dynamic 可以更简洁地表达这样的特定应用程序。
    • 你说得对,dynamic 在这里要好得多,它大大简化了反射。具体来说,它消除了类型上的ifs 和许多我不关心的as/is。我希望完全没有反思,而不是更多:)
    【解决方案3】:

    要确定该类型使用反射实现了一些通用接口:

    var objType = o.GetType();
    
    // get the ILookup<,> interface type (if the type implements it)
    var lookupInterface = objType.GetInterface("System.Linq.ILookup`2");
    
    // the type implemented the interface if returned non-null value
    var isILookup = lookupInterface != null;
    

    泛型类型的名称修改模式是

    type_name`generic_parameter_count
    

    对于泛型类型的特定实例:

    type_name`generic_parameter_count[type_name_1,...,type_name_n]
    

    在这种情况下,ILookup&lt;,&gt; 有两个参数,所以它是:

    System.Linq.ILookup`2
    

    我们对确切的实例不感兴趣,所以我们不需要指定类型参数。

    【讨论】:

    • @Ani:其他人都在想同样的事情......但是有更好的解决方案吗?
    • @mellamokb:是的,有。诀窍是在构造的泛型类型上调用GetGenericTypeDefinition
    • @Ani:它不会降低安全性或降低效率。如果您知道要查找的类型的确切名称,则无需搜索。这是一个选择,是的,但这种方法同样好。现在人们可能由于不熟悉或其他原因而对这种方法感到不舒服,这是可以理解的,但这并不会使它变得糟糕。
    • @Jeff:IMO,它确实降低了安全性。该名称可能有不同的接口。顺便说一句,我同意我所说的只是我的意见。这种技术更简洁,但当有其他替代方法可用时,我不喜欢使用字符串作为类型名称。
    • @Ani:如果我在这里使用完全限定的名称,则不会。这里没有歧义,所以这不是问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-12
    • 2019-12-05
    • 1970-01-01
    • 2013-07-06
    相关资源
    最近更新 更多