簡體   English   中英

在運行時使用C#設置或更改Attribute的屬性或字段。 可能?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM