在运行时修改类属性

8

我不确定是否有可能,但我看到过以下内容:
在运行时更改属性参数
我的情况非常相似,但我正在尝试在运行时更改类的属性:

[Category("Change me")]
public class Classic
{
    public string Name { get; set; }
}

其中一个答案是:

Dim prop As PropertyDescriptor = TypeDescriptor
    .GetProperties(GetType(UserInfo))("Age")
Dim att As CategoryAttribute = DirectCast(
     prop.Attributes(GetType(CategoryAttribute)),
     CategoryAttribute)
Dim cat As FieldInfo = att.GetType.GetField(
     "categoryValue",
      BindingFlags.NonPublic Or BindingFlags.Instance)
cat.SetValue(att, "A better description")

由于Marc Gravell的帮助,已更改为更易读的格式:

TypeDescriptor.AddAttributes(table, new Category{ Name = "Changed" });

使用TypeDescriptor时一切都很好,但是当使用:

var attrs = (Category[])typeof(Classic).GetCustomAttributes(
    typeof(Category),
    true);
attrs[0].Name

名称显示“Change me”的文本。
有没有办法在运行时更改此属性?

编辑:
我需要在Linq2Sql中使用设计器生成的代码,该代码具有DB模式。我想使用用户的默认模式,而不使用XML映射或更改生成的代码(表仍处于开发阶段并经常更改)。

设计器代码如下:

[global::System.Data.Linq.Mapping.TableAttribute(Name="DbSchema.MyTable")]
public partial class MyTable

我希望属性是:

[TableAttribute(Name="MyTable")] 

现在我已经深入研究了框架代码,我认为linq2sql使用的是:

TableAttribute[] attrs = (TableAttribute[])typeof(MyTable)
   .GetCustomAttributes(typeof(TableAttribute), true);

当我使用TypeDescriptor改变属性时,在GetCustomAttributes中的值并没有改变。

2
你为什么想要这样做?属性的作用是提供元数据,除此之外并没有太多用处。为什么不采取“规则列表”的方法,该列表最初由属性填充,并从那里进行更改呢? - vcsjones
你想要实现什么?在分类中本地化文本? - Sam B
@vcsjones 信不信由你,有时候你需要在运行时添加、更改或删除属性。我曾经不得不这样做,以便将转换器添加到 WPF 绑定中,使它们被序列化而不是被评估。 - user1228
我有点困惑 - 为什么你认为将“Classic”的“Name”更改会影响类别? - Marc Gravell
1
此外 - TypeDescriptor 代码本身非常脆弱;一个不错的解决方案是使用TypeDescriptor.AddAttributesTypeDescriptor.GetAttributes,而不是在属性实例上使用反射。 - Marc Gravell
我正在尝试更改linq2sql Designer属性 - 具体来说是将DB模式更改为配置值。Designer = [global::System.Data.Linq.Mapping.TableAttribute(Name="DbSchema.MyTable")] - Roy Dallal
3个回答

2
完全避免反射,可以通过TypeDescriptor实现:
using System;
using System.ComponentModel;
using System.Linq;
[Category("nice")]
class Foo {  }
static class Program
{
    static void Main()
    {
        var ca = TypeDescriptor.GetAttributes(typeof(Foo))
              .OfType<CategoryAttribute>().FirstOrDefault();
        Console.WriteLine(ca.Category); // <=== nice
        TypeDescriptor.AddAttributes(typeof(Foo),new CategoryAttribute("naughty"));
        ca = TypeDescriptor.GetAttributes(typeof(Foo))
              .OfType<CategoryAttribute>().FirstOrDefault();
        Console.WriteLine(ca.Category); // <=== naughty
    }
}

2
好吧,每当我使用TypeDescriptor时,它并不真正起作用,我得到的是更改后的值。但是linq2sql(我需要它)使用(TableAttribute [])table.GetCustomAttributes(typeof(TableAttribute),true),这将获取原始值... - Roy Dallal

0
如果您使用反射,则不完全像这样 - 反射属性无法被替换 - 只有组件模型视图受到 TypeDescriptor 的影响。但是,您可以为自己的目的子类化 CategoryAttribute。特别适用于 i18n。
using System.ComponentModel;
using System;
[MyCategory("Fred")]
class Foo {  }
static class Program
{
    static void Main()
    {
        var ca = (CategoryAttribute)Attribute.GetCustomAttribute(typeof(Foo), typeof(CategoryAttribute));
        Console.WriteLine(ca.Category);
              // ^^^ writes "I was Fred, but not I'm EVIL Fred"
    }
}
class MyCategoryAttribute : CategoryAttribute
{
    public MyCategoryAttribute(string category) : base(category) { }
    protected override string GetLocalizedString(string value)
    {
        return "I was " + value + ", but not I'm EVIL " + value;
    }
}

很遗憾,我无法更改原始属性或创建自己的版本。Linq2Sql需要TableAttribute。 - Roy Dallal

0

你需要编写代码以支持运行时值的属性。例如,验证属性通过设置资源类型和资源字符串来支持消息的国际化,而不是静态消息字符串。

另一种方法是使用IOC容器,如StructureMap或Unity,提供一些提供值的对象/服务。

如果您不想将属性与特定容器耦合,请使用模式和实践组提供的通用ServiceLocator包装器。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接