Можно ли передать аргумент атрибута из производного класса в его базовый класс?

Можно ли передать аргумент атрибута из производного класса в его базовый класс?

По сути, я пытаюсь установить аргумент атрибута свойства из производного класса.

  • Как это можно сделать на С++

    public class HasHistory<T, string name> {
        public HasHistory() {
            History=new History<T>();
        }
    
        // here's my attribute
        [BsonElement(name)]
        public History<T> History {
            get;
            protected set;
        }
    }
    

    Однако аргументы шаблона, не относящиеся к типу, допустимы в C++, но недопустимы в C#.

  • Какой неожиданный обходной путь в C#

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

    Я действительно хочу сделать этот вызов, потому что я хочу, чтобы конструктор базового класса инициализировал член; это на самом деле весь смысл базового класса.

    public class HasHistory<T> {
        public HasHistory() {
            // this will be called before Derived is constructed
            // and so the vtbl will point to the property method
            // defined in this class.
            // We could probably get away with this, but it smells.
            History=new History<T>();
        }
    
        // here's my property, without an Attribute
        public virtual History<T> History {
            protected set;
            get;
        }
    }
    
    public class Derived: HasHistory<SomeType> {
        // crap! I made this virtual and repeated the declaration
        // just so I could add an attribute!
        [BsonElement("SomeTypeHistory")]
        public virtual HasHistory<SomeType> History {
            protected set;
            get;
        }
    }
    

    Поэтому я полагаю, что не мог бы поместить атрибут в базу и вместо этого поместить его в свойство производного класса, которое использует/реализуется в терминах свойства защищенного базового класса, но это достаточно громоздко, что устраняет любое удобство, полученное при использовании базы класс.

Итак, есть хороший способ сделать это, верно? Верно?

Как переопределить атрибут свойства производного класса, который наследует базовый БЕЗ переопределения свойства в производном классе?


person tpdi    schedule 15.03.2013    source источник
comment
Не уверен, что вы делаете с частью <T, string name> в общем.   -  person Chris Sinclair    schedule 15.03.2013
comment
@ChrisSinclair Это неверно, но он пытается проиллюстрировать, что ему нужен механизм C #, такой как аргументы шаблона C ++.   -  person ta.speot.is    schedule 15.03.2013
comment
Вы пытались применить атрибут в базовом классе? Насколько я знаю, невозможно передать переменную в атрибут, так как атрибут обрабатывается во время компиляции, а не во время выполнения.   -  person Fendy    schedule 15.03.2013
comment
Вышеизложенное не имеет большого смысла. Переменные типа действительно больше похожи на заполнители, которые по запросу заменяются фактически запрашиваемым типом (когда создается экземпляр универсального класса). Например, если бы вы использовали string, тогда ваш атрибут отображал бы [BsonElement(string)], что недопустимо для компиляции.   -  person Kenneth K.    schedule 15.03.2013
comment
Чего вы надеетесь достичь с помощью этого атрибута, может быть, есть другой способ подойти к проблеме?   -  person R0MANARMY    schedule 15.03.2013
comment
вызов виртуальной функции в конструкторе Не совсем правильно. Вы устанавливаете виртуальное свойство, и в вашем случае это просто и не повредит.   -  person leppie    schedule 15.03.2013


Ответы (1)


Обновление: черт возьми, вы уже подумали об этом. надо было обновить перед публикацией :)

К сожалению, то, что вы хотите сделать, выходит далеко за рамки механизма атрибутов C#. Универсальные шаблоны отличаются от шаблонов, поэтому такой обходной путь хорош настолько, насколько это возможно.

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

Исходный ответ ниже...


Если я правильно понимаю пример, вы хотите применить атрибут к члену класса на основе некоторого значения, объявленного в производном типе. Поскольку C# не поддерживает нетиповые параметры для Generics, вам нужен другой способ сделать это.

Одна вещь, которую вы можете сделать, это переопределить свойство в классе-потомке следующим образом:

public class HasHistory<T>
{
    public HasHistory() 
    {
        History = new History<T>();
    }

    public virtual History<T> History { get; protected set; }
}

public class MyHistory<T> : HasHistory<T>
{
    public MyHistory()
        : base()
    {}

    [BSONElement("some name")]
    public override History<T> History 
    { 
        get
        {
            return base.History;
        }
        protected set
        {
            base.History = value;
        }
    }
}

Код, использующий атрибут BsonElement, будет работать с типом фактического производного экземпляра HasHistory<T>, поэтому он будет рассматривать атрибуты, определенные в конце цепочки virtual. Учитывая приведенный выше код, если я создам экземпляр MyHistory<T> и передам его сериализатору BSON, он найдет атрибут, прикрепленный к свойству History в классе MyHistory<T>.

Однако вы можете определить атрибут на базовом уровне и при необходимости переопределить его в производных классах. Не уверен, что это будет полезно в вашем случае.

Это больше работы, особенно потому, что вы должны делать это в каждом производном классе, но я думаю, что в этом случае вы максимально приблизитесь к стилю шаблона C++. Однако я был бы рад оказаться неправым :)

person Corey    schedule 15.03.2013
comment
Проблема здесь в том, что теперь у вас есть вызов виртуального метода в базовом конструкторе. Я не нашел решения для этого. - person nicodemus13; 17.10.2013
comment
Да, я заметил это после того, как опубликовал исходный ответ и обнаружил, что вы обновили вопрос. В основном это сводится к тому, что на данный момент это выходит за рамки возможностей языка. - person Corey; 17.10.2013
comment
Было бы неплохо, если бы был способ «переопределить» атрибуты... что бы это ни значило. - person nicodemus13; 17.10.2013
comment
Атрибуты — это украшения типов, а не экземпляров. Атрибут даже не может узнать, что он украшает, если вы явно не укажете его в качестве параметра вызова метода. Если вам нужны различия для конкретного экземпляра, вам нужно свойство. - person Corey; 17.10.2013
comment
И просто чтобы доказать, что C# более гибок, чем мы обычно думаем, и гораздо более опасен под капотом... это, вы можете сделать шаг ближе к тому, что вам нужно, используя TypeDescriptor для управления метаданными типа во время выполнения. См., например: geekswithblogs.net/abhijeetp/ архив/2009/01/10/ - person Corey; 17.10.2013
comment
конечно, так как же избежать вызова виртуального метода? Я просто лениво вынашивал неопределенную идею, чтобы избежать этой конкретной проблемы. - person nicodemus13; 17.10.2013
comment
so how does one avoid a virtual method call? Переместить свойство в родительский класс? Или просто используйте вызов виртуального метода. Это неодобрительно, но вы можете это сделать. - person Corey; 18.10.2013
comment
Можно, но это не очень хорошая идея. Дело в том, что я хотел бы, чтобы свойство было украшено различными атрибутами, в зависимости от дополнительных требований в производном классе. Я переделал его, чтобы избежать этой проблемы, поэтому я думаю, что это решение - использовать другой дизайн. ;) - person nicodemus13; 18.10.2013