在C#中在运行时设置或更改属性的属性或字段。可能?

时间:2009-12-03 10:24:22

标签: c# attributes properties field

我相信除了在构造函数中执行此操作之外,没有人类方法可以更改Attribute中的任何属性或字段。也就是说,没有自己重新设计和重新编译Visual Studio。这里已经发布了类似的问题: Change Attribute's parameter at runtime 但我相信我的问题的特点是不同的,需要一个新的帖子。

我使用枚举来跟踪DataTable的不同列。我在每个枚举元素中使用属性来指示基础类型和描述 - 如果由于允许命名枚举元素的刚性字符集(例如“Tomato_Field”),.ToString()会给出“丑陋”的结果。 “当你想要”番茄田“之类的时候。这允许我将所有相关信息放在同一个对象中,我相信它应该是什么。这样我以后可以使用简单干净的foreach创建所有列,循环遍历枚举的元素并提取metedata(描述和类型)以创建每个列。

现在,有些列是自动计算的,这意味着在创建期间 - 通过DataTable Identifier.Columns.Add。(NameOfColumn,underlyingType,optional:autocalculatedString) - 我需要指定一个字符串来确定如何计算它。该字符串必须使用其他列的名称,这些列可能位于Description属性中。看似合乎逻辑的方法是使用另一个保存字符串的属性,该属性应使用其他列的名称构建,需要访问元数据。现在,在构造函数中似乎不可能:您被迫提供一个常量字符串。你不能使用方法或任何东西。

如果有一种方法可以在运行时更改属性内的属性(让我们称之为AutocalculatedStringAttribute),则可以解决此问题。如果访问元数据,则可以检索在Attribute的构造函数中使用的字符串,当然可以更改该字符串。但是,如果稍后再次访问元数据,忽略更改,我相信每次在运行时访问元数据时都会调用构造函数,从而忽略任何更改。

当然,有很多方法可以实现我想要做的事情,但我的问题是,如果有一种方法可以正确使用属性。如果没有使用CodeDOM来重新编译整个程序集,并且AutocalculatedStringAttribute的构造函数发生了变化,那么就会出现一定的过度杀伤。

3 个答案:

答案 0 :(得分:1)

是的,用于初始化属性的元数据是不可变的。但是,您可以向属性类添加属性和方法,该属性类可以运行代码并在构造属性对象后返回相关信息。他们所依赖的数据不必存储在元数据中,可以在任何地方保存。

当然,这样的代码不必是属性类实现的一部分,它也可以是实例化属性的代码的一部分。它属于哪个地方。

答案 1 :(得分:0)

我不完全清楚哪些代码正在使用此属性,这很重要......

无法更改已刻录到代码中的属性 - 您可以使用反射查询它,但这是关于它的。但是,在许多情况下,您仍然可以做有趣的事情 - 我不知道它们是否适用于您的场景:

  • 你可以对许多属性进行子类化,例如[Description][DisplayName]等 - 当你将 in 一个常量字符串(通常是一个键)传递给.ctor时,它可以返回(通过常规C#)更灵活的值 - 可能从resx查找描述以实现i18n
  • 如果调用者尊重System.ComponentModel,您可以在运行时将属性轻松地附加到类型等非常 - 但在单个属性上更难,特别是在案例中DataTable等等(因为它有DataView的自定义描述符模型)
  • 您可以通过ICustomTypeDescriptor / TypeDescriptionProvider / PropertyDescriptor - 很多工作来包装和提供您自己的模型,但可以设置自己的属性,或返回属性
  • 的描述(等)

我不知道这有多少适合您的环境(也许会显示一些您拥有的和您想要的代码),但它突出显示(问题标题)是:有些事情你可以做调整属性在运行时感知的方式。

答案 2 :(得分:0)

我想将此作为评论发布,但由于我希望包含一些我无法提供的代码,因为600个字符的限制。这是我设法找到的最干净的解决方案,虽然它不包括在枚举上创建列的所有信息,这是我的目标。我翻译了每个字段,以便更容易理解。我没有展示一些具有明显用途的代码(特别是其他自定义属性的实现及其静态方法来检索元数据,假设它有效)。

这可以完成工作,但我希望在Autocalculated属性中包含存储在字符串“instancesXExpString”和“totalInstancesString”中的信息,该属性当前仅标记具有此类字符串的列。这是我无法做到的,我相信,通过子类化不能轻易实现 - 尽管这是一种巧妙的方法,我必须说。 谢谢你的两个迅速回复,顺便说一下。

没有任何进一步的麻烦,让我们来看看代码:

// Form in which the DataGridView, its underlying DataTable and hence the enumeration are:
public partial class MainMenu : Form {
(...)

    DataTable dt_expTable;

//Enum that should have all the info on its own... but does not:
public enum e_columns {
        [TypeAttribute(typeof(int))]
        Experiments = 0,

        [TypeAttribute(typeof(decimal))]
        Probability,

        [DescriptionAttribute("Samples / Exp.")]
        [TypeAttribute(typeof(int))]
        SamplesXExperiment,

        [DescriptionAttribute("Instances / Sample")]
        [TypeAttribute(typeof(int))]
        InstancesXSample,

        [DescriptionAttribute("Instances / Exp.")]
        [TypeAttribute(typeof(int))]
        [Autocalculated()]
        InstancesXExp,

        [DescriptionAttribute("Total Instances")]
        [TypeAttribute(typeof(long))]
        [Autocalculated()]
        Total_Instances
    };

//These are the two strings
string instancesXExpString = "[" + DescriptionAttribute.obtain(e_columns.SamplesXExperiment) + "] * [" + DescriptionAttribute.obtain(e_columns.InstancesXMuestra) + "]";
    string totalInstancesString = "[" + DescriptionAttribute.obtain(e_columns.InstancesXExp) + "] * [" + DescriptionAttribute.obtain(e_columns.Experiments) + "]";

public MainMenu() {
        InitializeComponent();
    (...)        
    }

private void MainMenu_Load(object sender, EventArgs e) {
    (...)
    // This is the neat foreach I refered to:
    foreach (e_columns en in Enum.GetValues(typeof(e_columnas))) {
                addColumnDT(en);
        }
}

private void addColumnDT(Enum en) {
    //*This is a custom static method for a custom attrib. that simply retrieves the description string or
    //the standard .ToString() if there is no such attribute.*/
            string s_columnName = DescriptionAttribute.obtain(en);
            bool b_typeExists;
            string s_calculusString;
            Type TypeAttribute = TypeAttribute.obtain(en, out b_typeExists);
            if (!b_typeExists) throw (new ArgumentNullException("Type has not been defined for one of the columns."));
            if (isCalculatedColumn(DescriptionAttribute.obtain(en))) {
                s_calculusString = calcString(en);
                dt_expTable.Columns.Add(s_columnName, TypeAttribute, s_calculusString);
            } else {
                dt_expTable.Columns.Add(s_columnName, TypeAttribute);
            }
    }

private string calcString(Enum en) {
        if (en.ToString() == e_columns.InstancessXExp.ToString()) {
            return instancesXExpString;
        } else if (en.ToString() == e_columns.Total_Samples.ToString()) {
            return totalInstancesString;
        } else throw (new ArgumentException("There is a column with the autocalculated attribute whose calculus string has not been considered."));
    }
(...)
}

我希望这段代码能够澄清情况以及我想要做的事情。