动态设置泛型类型参数 [英] Dynamically set generic type argument

查看:133
本文介绍了动态设置泛型类型参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是我的问题,下面就<一href="http://stackoverflow.com/questions/2983810/does-ms-test-provide-a-default-value-equals-comparison">here,我试图创建一个通用的值相等比较。我从来没有玩过之前的反射所以不知道如果我在正确的轨道,但无论如何,我有这个想法至今:

Following on from my question here, I'm trying to create a generic value equality comparer. I've never played with reflection before so not sure if I'm on the right track, but anyway I've got this idea so far:

bool ContainSameValues<T>(T t1, T t2)
{
    if (t1 is ValueType || t1 is string)
    {
        return t1.Equals(t2);
    }

    else 
    {
        IEnumerable<PropertyInfo> properties = t1.GetType().GetProperties().Where(p => p.CanRead);
        foreach (var property in properties)
        {
            var p1 = property.GetValue(t1, null);
            var p2 = property.GetValue(t2, null);

            if( !ContainSameValues<p1.GetType()>(p1, p2) )
                return false;
        }
    }
    return true;
}

这不编译,因为我不知道如何设置的T​​类型的递归调用。是否可以动态地在所有做到这一点?

This doesn't compile because I can't work out how to set the type of T in the recursive call. Is it possible to do this dynamically at all?

有一对夫妇在这里的相关问题,我曾经读过的,但我不能跟着他们足够的工作如何,他们可能在我的情况下适用。

There are a couple of related questions on here which I have read but I couldn't follow them enough to work out how they might apply in my situation.

推荐答案

您可以避免调用反射,如果你是幸福的基础上,静态知道类型的属性进行比较。

You can avoid reflection on invocation if you are happy to compare based on the statically know types of the properties.

这依赖于前pressions 3.5做一次性反射以简单的方式,有可能做得更好,以减少工作量极嵌套类型,但是这应该是适用于大多数的需求。

This relies on Expressions in 3.5 to do the one off reflection in a simple manner, it is possible to do this better to reduce effort for extremely nested types but this should be fine for most needs.

如果你就必须关闭运行时类型将需要反映一定程度的(虽然这将是便宜的,如果你再每个属性访问和比较方法缓存),但这本质上是要复杂得多,因为在子性质的运行时类型可能不匹配的话,对通用化,你就不得不考虑类似如下的规则:

If you must work off the runtime types some level of reflection will be required (though this would be cheap if you again cache the per property access and comparison methods) but this is inherently much more complex since the runtime types on sub properties may not match so, for full generality you would have to consider rules like the following:

  • 在考虑不匹配的类型不等于
    • 简单易懂,易于实施
    • 不太可能成为一个有用的操作
    • consider mismatched types to NOT be equal
      • simple to understand and easy to implement
      • not likely to be a useful operation
      • 再简单,有些比较难实现。
      • 在复杂的,不是真正可怕的意义
      • 在复杂的,标题为鸭打字

      有多种其他的选择,但是这应该是深思,为什么完全运行时分析是很难的。

      There are a variety of other options but this should be food for thought as to why full runtime analysis is hard.

      (注意,我已经改变了你'叶'终止后卫是什么,我认为是卓越的,如果你想只用螫/值类型出于某种原因感到自由)

      (note that I have changed you 'leaf' termination guard to be what I consider to be superior, if you want to just use sting/value type for some reason feel free)

      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Text;
      using System.Reflection;
      using System.Linq.Expressions;
      
      
      class StaticPropertyTypeRecursiveEquality<T>
      {
          private static readonly Func<T,T, bool> actualEquals;
      
          static StaticPropertyTypeRecursiveEquality()
          {
              if (typeof(IEquatable<T>).IsAssignableFrom(typeof(T)) || 
                  typeof(T).IsValueType ||
                  typeof(T).Equals(typeof(object)))
              {
                  actualEquals = 
                      (t1,t2) => EqualityComparer<T>.Default.Equals(t1, t2);
              }
              else 
              {
                  List<Func<T,T,bool>> recursionList = new List<Func<T,T,bool>>();
                  var getterGeneric = 
                      typeof(StaticPropertyTypeRecursiveEquality<T>)
                          .GetMethod("MakePropertyGetter", 
                              BindingFlags.NonPublic | BindingFlags.Static);
                  IEnumerable<PropertyInfo> properties = typeof(T)
                      .GetProperties()
                      .Where(p => p.CanRead);
                  foreach (var property in properties)                
                  {
                      var specific = getterGeneric
                          .MakeGenericMethod(property.PropertyType);
                      var parameter = Expression.Parameter(typeof(T), "t");
                      var getterExpression = Expression.Lambda(
                          Expression.MakeMemberAccess(parameter, property),
                          parameter);
                      recursionList.Add((Func<T,T,bool>)specific.Invoke(
                          null, 
                          new object[] { getterExpression }));                    
                  }
                  actualEquals = (t1,t2) =>
                      {
                          foreach (var p in recursionList)
                          {
                              if (t1 == null && t2 == null)
                                  return true;
                              if (t1 == null || t2 == null)
                                  return false;
                              if (!p(t1,t2))
                                  return false;                            
                          }
                          return true;
                      };
              }
          }
      
          private static Func<T,T,bool> MakePropertyGetter<TProperty>(
              Expression<Func<T,TProperty>> getValueExpression)
          {
              var getValue = getValueExpression.Compile();
              return (t1,t2) =>
                  {
                      return StaticPropertyTypeRecursiveEquality<TProperty>
                          .Equals(getValue(t1), getValue(t2));
                  };
          }
      
          public static bool Equals(T t1, T t2)
          {
              return actualEquals(t1,t2);
          }
      }
      

      有关测试我使用了以下内容:

      for testing I used the following:

      public class Foo
      {
          public int A { get; set; }
          public int B { get; set; }
      }
      
      public class Loop
      {
          public int A { get; set; }
          public Loop B { get; set; }
      }
      
      public class Test
      {
          static void Main(string[] args)
          {
              Console.WriteLine(StaticPropertyTypeRecursiveEquality<String>.Equals(
                  "foo", "bar"));
              Console.WriteLine(StaticPropertyTypeRecursiveEquality<Foo>.Equals(
                  new Foo() { A = 1, B = 2  },
                  new Foo() { A = 1, B = 2 }));
              Console.WriteLine(StaticPropertyTypeRecursiveEquality<Loop>.Equals(
                  new Loop() { A = 1, B = new Loop() { A = 3 } },
                  new Loop() { A = 1, B = new Loop() { A = 3 } }));
              Console.ReadLine();
          }
      }
      

      这篇关于动态设置泛型类型参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆