Установите или измените свойства или поля атрибута во время выполнения в C #. Возможный? - PullRequest
1 голос
/ 03 декабря 2009

Я считаю, что нет никакого человеческого способа изменить какой-либо атрибут или поле внутри атрибута, кроме как сделать это в конструкторе. То есть, если не пересматривать и не перекомпилировать Visual Studio самостоятельно. Здесь уже есть похожий вопрос: Изменить параметр атрибута во время выполнения но я считаю, что особенности моей проблемы достаточно разные, чтобы требовать нового поста.

Я использую перечисление для отслеживания различных столбцов DataTable. Я использую атрибуты в каждом элементе перечисления, чтобы указать базовый тип и описание. В этом случае .ToString () даст «ужасный» результат из-за жесткого набора символов, которым разрешено именовать элемент перечисления, например, «Tomato_Field». "когда вы хотите" Томатное поле "и тому подобное. Это позволяет мне размещать всю связанную информацию в одном и том же объекте, что, как я считаю, должно быть. Таким образом я позже смогу создать все столбцы с простым и чистым foreach, который циклически перебирает элементы перечисления и извлекает метеданные (описание и тип) для создания каждого столбца.

Теперь некоторые столбцы автоматически рассчитываются, что означает, что при их создании -via DataTable Identifier.Columns.Add. (NameOfColumn, basicType, необязательный: autocalculatedString) - мне нужно указать строку, которая определяет способ ее вычисления , Эта строка должна использовать имена других столбцов, которые могут быть в Атрибуте Description. Подход, который выглядит логичным, заключается в использовании другого атрибута, который содержит строку, которая должна быть построена с использованием имен других столбцов, требующих доступа к метаданным. Теперь это кажется невозможным в конструкторе: вы вынуждены предоставить постоянную строку. Вы не можете использовать метод или что-нибудь.

Эту проблему можно решить, если во время выполнения есть способ изменить свойство внутри атрибута (назовем его AutocalculatedStringAttribute). Если у вас есть доступ к метаданным, вы можете получить строку, которую вы использовали в конструкторе Атрибута, и вы, конечно, можете изменить эту строку. Однако, если вы позже получите доступ к метаданным снова, это изменение игнорируется, я считаю, что конструктор вызывается каждый раз, когда к метаданным обращаются во время выполнения, таким образом игнорируя любые изменения.

Есть, конечно, грязные способы достичь того, что я пытаюсь сделать, но мой вопрос заключается в том, есть ли способ правильно использовать атрибуты для этого. Если не прибегать к CodeDOM для перекомпиляции всей сборки с измененным конструктором AutocalculatedStringAttribute, это определенное излишество.

Ответы [ 3 ]

1 голос
/ 03 декабря 2009

Правильно, метаданные, которые используются для инициализации атрибута, являются неизменяемыми. Но вы можете добавить свойства и методы в класс атрибута, который может выполнять код и возвращать соответствующую информацию после создания объекта атрибута. Данные, на которые они полагаются, не нужно хранить в метаданных, их можно сохранить где угодно.

Конечно, такой код не должен был бы быть частью реализации класса атрибута, он мог бы также быть частью кода, который создает экземпляр атрибута. Который, где он принадлежит.

0 голосов
/ 03 декабря 2009

Я хотел опубликовать это как комментарий, но так как я хотел включить код, который я не смог, учитывая ограничение в 600 символов. Это самое чистое решение, которое мне удалось найти, хотя оно не включает всю информацию для создания столбцов в enum, что является моей целью. Я перевел каждое поле, чтобы было легче следить. Я не показываю некоторый код, который имеет очевидное использование (в частности, реализации других пользовательских атрибутов и их статических методов для извлечения метаданных, предположим, что это работает).

Это выполняет работу, но в идеале я хотел бы включить информацию, хранящуюся в строках "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."));
    }
(...)
}

Надеюсь, этот фрагмент кода прояснит ситуацию и то, что я пытаюсь сделать.

0 голосов
/ 03 декабря 2009

Мне не совсем понятно, какой код потребляет этот атрибут, и это важно ...

Вы не можете изменить атрибут, который записан в коде - вы можетезапросить это с отражением, но это об этом.Однако во многих случаях вы все еще можете делать интересные вещи - я не знаю, применимы ли они к вашему сценарию, хотя:

  • вы можете создавать подклассы многих атрибутов, таких как [Description], [DisplayName] и т. Д.- и пока вы передаете в константную строку (обычно ключ) в .ctor, он может возвращать (через обычный C #) более гибкие значения - возможно, просматривая описание из resx для реализации i18n
  • если собеседник соблюдает System.ComponentModel, вы можете прикрепить атрибуты во время выполнения к типам и т. Д. очень легко - но гораздо сложнее для отдельных свойств, особенно в случае DataTable и т. д. (поскольку у него есть пользовательская модель дескриптора через DataView)
  • , вы можете обернуть вещи и предоставить свою собственную модель через ICustomTypeDescriptor / TypeDescriptionProvider / PropertyDescriptor - лоты работы, но предоставляет доступ к настройке собственных атрибутов или возвращает описание (и т. д.) за пределами атрибутов

Я не знаю, насколько это подходит для вашегоокружение (возможно, показать какой-то кодто, что у вас есть и что вы хотите), но это подчеркивает, что (по поводу названия вопроса) да: есть вещи, которые вы можете сделать, чтобы настроить восприятие атрибутов во время выполнения.

...