[英]Set or change Attribute's properties or fields at runtime in C#. Possible?
我相信除了在構造函數中進行更改外,沒有人為的方法可以更改屬性內的任何屬性或字段。 也就是說,您自己無需重新設計和重新編譯Visual Studio。 這里已經發布了一個類似的問題: 在運行時更改屬性的參數,但是我認為我的問題的特殊性足以要求新的帖子。
我使用枚舉來跟蹤DataTable的不同列。 我在每個枚舉元素中使用屬性來指示基礎類型和描述-萬一.ToString()由於允許命名枚舉元素的剛性字符集而產生“難看”的結果,例如“ Tomato_Field” ”(例如“ Tomato Field”等)。 這使我可以將所有相關信息放置在同一對象中,我相信它應該是。 這樣,我以后可以使用簡單干凈的foreach創建所有列,該foreach循環遍歷枚舉的元素並提取元數據(描述和類型)以創建每個列。
現在,某些列是自動計算的,這意味着在創建它們時-通過DataTable Identifier.Columns.Add。(NameOfColumn,underlyingType,可選:autocalculatedString)-我需要指定一個字符串來確定應如何計算。 該字符串必須使用其他列的名稱,這些名稱可能在Description屬性中。 看起來合乎邏輯的方法是使用另一個保存字符串的屬性,該屬性應使用其他列的名稱來構建,從而需要訪問元數據。 現在在構造函數中這似乎是不可能的:您被迫提供一個常量字符串。 您不能使用任何方法。
如果有一種方法可以在運行時更改屬性內部的屬性(稱為AutocalculatedStringAttribute),則可以解決此問題。 如果訪問元數據,則可以檢索在Attribute的構造函數中使用的字符串,當然也可以更改該字符串。 但是,如果以后再訪問元數據時,更改將被忽略,我相信每次在運行時訪問元數據時都會調用構造函數,從而忽略任何更改。
當然,有一些骯臟的方法可以達到我想要做的事情,但是我的問題是,是否有一種適當地使用屬性的方法。 由於沒有使用CodeDOM來重新編譯整個程序集,而更改了AutocalculatedStringAttribute的構造函數,因此有些過分了。
正確,用於初始化屬性的元數據是不可變的。 但是,您可以向屬性類添加屬性和方法,這些屬性和方法可以運行代碼並在構造屬性對象后返回相關信息。 他們所依賴的數據不必存儲在元數據中,可以保存在任何地方。
當然,這些代碼不必一定是屬性類實現的一部分,它也可以是實例化屬性的代碼的一部分。 它屬於哪一個。
我尚不清楚什么代碼正在使用此屬性,這很重要...
您不能更改已刻錄到代碼中的屬性-您可以使用反射來查詢它,但僅此而已。 但是,在許多情況下,您仍然可以做一些有趣的事情-不過,我不知道它們是否適用於您的方案:
[Description]
, [DisplayName]
等許多屬性作為子類- 在將常量字符串(通常是鍵)傳遞給.ctor的同時,它可以返回(通過常規C#)更靈活的值-也許可以查找從resx實現i18n的描述 System.ComponentModel
,你可以在運行時附加屬性類型等很容易-但是,單個屬性更難, 尤其是在的情況下, DataTable
等(因為有通過自定義描述模型DataView
) ICustomTypeDescriptor
/ TypeDescriptionProvider
/ PropertyDescriptor
提供您自己的模型- 很多工作,但是提供了設置自己的屬性或在屬性之外返回描述(等)的權限 我不知道其中有多少適合您的環境(也許顯示了您所擁有和想要的一些代碼),但是它突出顯示了(是問題名稱)是的:您可以做一些事情來調整屬性在運行時感知 。
我想發表此評論,但由於要限制600個字符,因此我想包含一些我不能包含的代碼。 這是我設法找到的最干凈的解決方案,盡管它不包括在枚舉上創建列的所有信息,這是我的目標。 我已翻譯了每個字段,以使其易於理解。 我沒有顯示一些有明顯用途的代碼(特別是其他自定義屬性的實現及其用於檢索元數據的靜態方法,假設它可以工作)。
這樣就可以完成工作,但是理想情況下,我希望將存儲在字符串“ instancesXExpString”和“ totalInstancesString”中的信息包括在Autocalculated屬性中,該屬性當前僅標記具有此類字符串的列。 這是我一直無法做到的,並且我相信不能通過子類化輕松地完成-盡管這是一種巧妙的方法,我必須說。 謝謝您的兩個迅速答復,順便說一句。
事不宜遲,讓我們看一下代碼:
// 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."));
}
(...)
}
我希望這段代碼可以澄清這種情況以及我想做的事情。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.