我相信除了在构造函数中执行此操作之外,没有人类方法可以更改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的构造函数发生了变化,那么就会出现一定的过度杀伤。
答案 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."));
}
(...)
}
我希望这段代码能够澄清情况以及我想要做的事情。