【问题标题】:Change a Property's type using reflection使用反射更改属性的类型
【发布时间】:2015-01-21 18:24:32
【问题描述】:

假设我有以下课程:

public class UniqueEntity {
    public object Key { get; set; }
}

我想要的是能够在运行时将Key 的类型从System.Object 更改为System.Int32

我猜想实现这一点的唯一方法(如果可以的话)是使用 reflection

属性的类型可以通过PropertyInfoPropertyType的属性获取(抱歉冗余)。

typeof(UniqueEntity).GetProperty("Key").PropertyType;

问题是它是只读的。我无法将值设置为PropertyType = typeof(int);

有没有办法改变属性的类型?

【问题讨论】:

  • 记录在案。我想这样做是因为Key 将使用属性进行注释。外部框架将识别该属性并从PropertyType 获取其类型。如果返回System.Int32 而不是System.Object,那就太好了
  • 有趣的问题。我不相信你可以在运行时做到这一点。但是作为一个对象,您可以在其中存储任何内容,然后进行运行时转换。
  • 不可能,但您可以继承此类并创建转换该属性的新属性,并且您只使用新属性。
  • 这样做你会得到什么?任何使用该属性的代码都将假定类型为Object,并且不会调用仅对Int32 或算术运算符有效的方法的正确调用。这看起来像是 XY 问题的经典案例:meta.stackexchange.com/questions/66377/what-is-the-xy-problem

标签: c# .net reflection types properties


【解决方案1】:

好的,所以...

主要问题是我有这门课:

public class UniqueEntity {
    [key]
    public object Key { get; set; }
}

但是EntityFramework 在处理object 键时会搞乱表的创建。我的想法是键可以是任何类型,由子类指定。

我开始使用泛型:

public class UniqueEntity<T> where T : IComparable, IComparable<T>
{
     [Key]
     public T Key { get; set; }
}

这在开始时效果很好,但后来在创建我的通用存储库时导致了严重的设计问题(存储库定义类似于 IRepository&lt;T,E&gt;,而 IRepository&lt;E&gt; 就足够了。


因此,EntityFramework 使用带有Key 属性注释的属性的PropertyInfo 值来获取实体的键类型。如果我可以将该类型更改为用户想要的任何类型(在运行时),并且完全避免使用泛型,那就太好了。


事实证明你不能删除一个属性,或者改变它的类型buuuuuut..你实际上可以用自定义属性创建一个新的虚拟属性,然后从原来的Key属性中删除属性!!

为此,我们需要一个CustomReflectionContext (System.Reflection.Context):

public class Modifier : CustomReflectionContext
{
    private string propName;
    private object propValue;

    public Modifier(string propName, object propValue)
    {
        this.propName = propName;
        this.propValue = propValue;
    }

    protected override IEnumerable<PropertyInfo> AddProperties(Type type)
    {
        Type newType = MapType(propValue.GetType().GetTypeInfo());
        return CreateProperty(newType, propName, o => propValue, (o, v) => propValue = v,
                                    new Attribute[] { new KeyAttribute() }, new Attribute[] { }, new Attribute[] { });
    }

    protected override IEnumerable<object> GetCustomAttributes(MemberInfo member, IEnumerable<object> declaredAttributes)
    {
        return new Attribute[] { };
    }
}

在这个小类的帮助下,我们能够创建自己的自定义类型以提供给EntityFramework,并使用现在具有[Key] 注释的新属性:

    var context = new Modifier("NewKey", 0);
    var uniqueEntityType = context.MapType(typeof(UniqueEntity).GetTypeInfo());
    var keyType = uniqueEntityType.GetProperty("Key").PropertyType;
    var keyAttrs = uniqueEntityType.GetProperty("Key").GetCustomAttributes();
    var newKeyType = uniqueEntityType.GetProperty("NewKey").PropertyType;
    var newKeyAttrs = uniqueEntityType.GetProperty("NewKey").GetCustomAttributes();
    Console.WriteLine("Key Property. Type: {0}, Attribute: {1}", keyType, keyAttrs.FirstOrDefault());
    Console.WriteLine("NewKey Property. Type: {0}, Attribute: {1}", newKeyType, newKeyAttrs.FirstOrDefault());
    Console.ReadLine();

【讨论】:

    【解决方案2】:

    没有。任何类成员的类型,无论是字段、属性还是方法返回值,以及任何方法参数的类型,都是在编译时确定的。

    在您的示例中,您可以在属性中存储Int32。它将被“装箱”,您可以根据需要将其转换回Int32。但 属性 本身的类型将保持为 Object

    【讨论】:

    • 问题是外部框架使用PropertyType 中的值来完成它的工作。如果它的System.Object 会搞砸。
    • @MatiCicero:你不能为这个属性创建不同类型的不同类吗?你可以使用属性来调整框架的行为吗?框架中是否有可让您进行自定义处理的钩子?
    • @MatiCicero:对不起。具体问题不会改变语言的规则。听起来您必须创建一个“shim”对象,该对象可以充当外部框架和内部实体类型之间的代理。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-05
    • 1970-01-01
    • 2016-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多