【问题标题】:Convert PropertyInfo into Func<> delegate将 PropertyInfo 转换为 Func<> 委托
【发布时间】:2014-04-19 10:23:00
【问题描述】:

我有许多数据模型对象,它们的主键字段用 DataAnnotations.KeyAttributes 修饰:

public class Example
{
  [Key]
  [Column("FirstKey", Order = 1)]
  public Guid FirstKey { get; set; }

  [Key]
  [Column("SecondKey", Order = 2)]
  public Guid SecondKey { get; set; }

  [Column("Unimportant", Order = 3)]
  public int UnimportantField {get; set; }
}

我可以使用 PropertInfo 选择数据模型对象的 Key 字段:

public static IEnumerable<PropertyInfo> GetKeyProperties<T>(this T source)
{
  PropertyInfo[] properties = source.GetType().GetProperties();
  return properties.Where(p => Attribute.IsDefined(p, typeof(KeyAttribute)));
}

我还可以手动构造一个匿名对象,其中包含给定类的 Key 字段:

public static object GetKeyObjectForExample(Example example)
{
  return new { example.FirstKey, example.SecondKey };
}

我想创建一个通用方法来构造一个 Func 委托,该委托将返回一个包含 Key 字段的匿名对象。

public static Func<T, object> GetKeyFunction<T>(this T source)
{
  //construct Func<T, object> using IEnumerable<PropertyInfo>?
}

因此,为给定的 Example 实例调用 GetKeyFunction 方法将返回一个 Func 委托。此委托应产生与 GetKeyObjectForExample 方法等效的输出。

如何实现 GetKeyFunction 方法?

【问题讨论】:

  • 我不认为您可以动态创建匿名类型。但是,您可以改为返回 dynamic。
  • 您不能将匿名对象作为返回类型或参数传递。你可以返回一个元组
  • Yuval - 感谢您的反馈。匿名类型是直接从 object 派生的类类型,并且不能转换为除 object 之外的任何类型 (msdn.microsoft.com/en-us/library/bb397696.aspx)。我在我当前的生产代码中将它们作为对象传递,所以它工作正常。
  • 你在将它们转换为对象时失去了意义,那么你如何访问它们的属性呢?
  • Yuval - 我不访问他们的属性。相反,我使用它们来检查与其他身份对象的等效性。所以我可能会通过传入一个关键对象并询问每个对象的关键对象是否等于传入的对象来搜索对象列表。

标签: c#


【解决方案1】:

不确定您想要实现的确切目标,因为即使使用动态或匿名对象,无论如何您都需要知道 Key 字段的名称,但是这样的事情会为您声明的方法(错误处理等省略):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Dynamic;

namespace ConsoleApplication11
{
    class KeyAttribute : Attribute
    {
    }
    public class Example
    {
        [Key]
        public Guid FirstKey { get; set; }

        [Key]
        public Guid SecondKey { get; set; }

        public int UnimportantField { get; set; }
    }

    static class Program
    {
        static void Main(string[] args)
        {
            Example e = new Example()
            {
                FirstKey = Guid.NewGuid(),
                SecondKey = Guid.NewGuid()
            };

            var func = GetKeyFunction<Example>();

            var key = func(e);
            Console.WriteLine(key.FirstKey);
            Console.WriteLine(key.SecondKey);
        }

        public static Func<T, dynamic> GetKeyFunction<T>()
        {
            return (t) =>
                {
                    var dyn = (IDictionary<string, object>)new ExpandoObject();
                    foreach (var p in GetKeyProperties(typeof(T)))
                    {
                        dyn.Add(p.Name, p.GetValue(t));
                    }
                    return dyn;
                };
        }

        public static IEnumerable<PropertyInfo> GetKeyProperties(Type t)
        {
            PropertyInfo[] properties = t.GetProperties();
            return properties.Where(p => p.GetCustomAttributes<KeyAttribute>().Any());
        }
    }
}

编辑

在阅读了一些 cmets 之后,您可能会喜欢这种方法并将 Equals 和 GetHashCode 实现添加到您的动态对象(此实现假定键字段的名称在相等中很重要)。感觉确定对象相等性的代码和动态太多,但是...

与上面相同的代码,但有一个帮助类来确定字典相等性和哈希码。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Dynamic;

namespace ConsoleApplication11
{
    class KeyAttribute : Attribute
    {
    }
    public class Example
    {
        [Key]
        public Guid FirstKey { get; set; }

        [Key]
        public Guid SecondKey { get; set; }

        public int UnimportantField { get; set; }
    }

    static class Program
    {
        static void Main(string[] args)
        {
            Example e = new Example()
            {
                FirstKey = Guid.NewGuid(),
                SecondKey = Guid.NewGuid(),
                UnimportantField = 0
            };

            var func = GetKeyFunction<Example>();

            var key = func(e);
            Console.WriteLine(key.FirstKey);
            Console.WriteLine(key.SecondKey);

            Console.WriteLine(key.Equals(key));
            Console.WriteLine(key.GetHashCode());

            Example e2 = new Example()
            {
                FirstKey = Guid.NewGuid(),
                SecondKey = Guid.NewGuid(),
                UnimportantField = 1
            };

            var key2 = func(e2);
            Console.WriteLine(key2.FirstKey);
            Console.WriteLine(key2.SecondKey);
            Console.WriteLine(key.Equals(key2));
            Console.WriteLine(key2.GetHashCode());
        }

        public static Func<T, dynamic> GetKeyFunction<T>()
        {
            return (t) =>
                {
                    var dyn = (IDictionary<string, object>)new ExpandoObject();
                    foreach (var p in GetKeyProperties(typeof(T)))
                    {
                        dyn.Add(p.Name, p.GetValue(t));
                    }
                    dyn.Add("Equals", (Func<object, bool>)((o) => 
                        {
                            var comparer = new DictionaryComparer<string,object>();
                            return comparer.Equals(dyn as IDictionary<string,object>, o as IDictionary<string,object>);
                        }
                        ));

                    dyn.Add("GetHashCode", (Func<int>)(() =>
                        {
                            var comparer = new DictionaryComparer<string, object>();
                            return comparer.GetHashCode(dyn as IDictionary<string, object>);
                        }
                        ));
                    return dyn;
                };
        }

        public static IEnumerable<PropertyInfo> GetKeyProperties(Type t)
        {
            PropertyInfo[] properties = t.GetProperties();
            return properties.Where(p => p.GetCustomAttributes<KeyAttribute>().Any());
        }
    }

    public class DictionaryComparer<TKey, TValue> : EqualityComparer<IDictionary<TKey, TValue>>
    {
        public DictionaryComparer()
        {
        }
        public override bool Equals(IDictionary<TKey, TValue> x, IDictionary<TKey, TValue> y)
        {
            // early-exit checks
            if (object.ReferenceEquals(x, y))
                return true;

            if (null == x || y == null)
                return false;

            if (x.Count != y.Count)
                return false;

            // check keys are the same
            foreach (TKey k in x.Keys)
                if (!y.ContainsKey(k))
                    return false;

            // check values are the same
            foreach (TKey k in x.Keys)
            {
                TValue v = x[k];
                if (object.ReferenceEquals(v, null))
                    return object.ReferenceEquals(y[k], null);


                if (!v.Equals(y[k]))
                    return false;
            }
            return true;
        }    

        public override int GetHashCode(IDictionary<TKey, TValue> obj)
        {
            if (obj == null)
                return 0;

            int hash = 0;

            foreach (KeyValuePair<TKey, TValue> pair in obj)
            {
                int key = pair.Key.GetHashCode(); // key cannot be null
                int value = pair.Value != null ? pair.Value.GetHashCode() : 0;
                hash ^= ShiftAndWrap(key, 2) ^ value;
            }

            return hash;
        }

        private static int ShiftAndWrap(int value, int positions)
        {
            positions = positions & 0x1F;


            // Save the existing bit pattern, but interpret it as an unsigned integer. 
            uint number = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
            // Preserve the bits to be discarded. 
            uint wrapped = number >> (32 - positions);
            // Shift and wrap the discarded bits. 
            return BitConverter.ToInt32(BitConverter.GetBytes((number << positions) | wrapped), 0);
        }
    }
}

【讨论】:

    猜你喜欢
    • 2015-07-08
    • 1970-01-01
    • 2012-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-03
    • 1970-01-01
    相关资源
    最近更新 更多