Есть ли способ перехватить сеттеры и геттеры в С#?

Как в Ruby, так и в PHP (и, я думаю, в других языках тоже) есть некоторые служебные методы, которые вызываются всякий раз, когда устанавливается свойство. ( *instance_variable_set* для Ruby, *__set* для PHP).

Итак, скажем, у меня есть такой класс С#:

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

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

Это возможно?


Изменить. Я хочу сделать это без определения резервного поля.


person Edgar Gonzalez    schedule 17.05.2011    source источник
comment
Показанный вами метод в любом случае реализует резервное поле в MSIL, так почему бы вам не захотеть его?   -  person Mr47    schedule 17.05.2011
comment
@ Mr47 Конечно, но я не хочу определять это сам. Я хочу автоматический способ сделать это.   -  person Edgar Gonzalez    schedule 17.05.2011
comment
Боюсь, что любой другой способ будет более громоздким, чем просто наличие фонового поля.   -  person Mr47    schedule 17.05.2011


Ответы (10)


Обычно нет; хотя несколько вариантов;

  • наследовать от ContextBoundObject, который делает позволить это, но за счет производительности
  • написать явное свойство (т.е. с резервным полем) и добавить вызов служебного метода вручную
  • просмотрите программы-ткачи времени компиляции, такие как PostSharp — обычно путем обнаружения атрибута или подобного
  • посмотрите на генераторы кода времени выполнения, предлагаемые некоторыми инструментами DI/IoC (и некоторыми другими инструментами на основе «декораторов»), которые либо украшают, либо подклассируют ваш объект, чтобы добавить дополнительный код
person Marc Gravell    schedule 17.05.2011
comment
Можете ли вы предоставить ссылку, которая показывает, как это сделать через ContextBoundObject? Спасибо. - person Cheng Chen; 17.05.2011
comment
Я думаю, что DynamicObject также можно использовать в этом случае (переопределяя TrySetMember). Но как ContextBoundObject? - person Cheng Chen; 17.05.2011
comment
@Danny - в конечном счете, свойства - это методы, поэтому должно работать что-то вроде этого: codeproject.com/ КБ/cs/aspectintercept.aspx - person Marc Gravell; 17.05.2011

Это можно сделать непосредственно в самом теле свойства, но тогда вам нужно использовать правильное резервное поле вместо автоматически реализуемые свойства.

private string firstName;

public string FirstName 
{ 
  get { return firstName;} 
  set 
  {
    if(check(value))
    {
       firstName = value;
    }
  } 
}

Даже с автоматически реализуемыми свойствами вы получаете резервное поле — оно генерируется компилятором, и у вас нет к нему прямого доступа.


Редактировать:

Поскольку вам не нужно резервное поле, у вас есть другие варианты — с этим может помочь использование инструмента АОП, такого как PostSharp.

person Oded    schedule 17.05.2011

Для этого вам придется написать свойства полностью.

person Mr47    schedule 17.05.2011

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

public class Person
{
    private 
    public string FirstName
    {
        get
        {
            return _firstName;
        }
        set
        {
            // see how we can call a method below? or any code for that matter..
            _firstName = SanitizeName(value);
        }
    }
}
person Jeff LaFay    schedule 17.05.2011

Вы в состоянии переписать свой класс для реализации интерфейса? Если это так, перехватчик интерфейса Unity может дать вам то, что вам нужно. Если интерфейс не подходит, эта ссылка также документирует перехватчики типов и экземпляров Unity.

person Richard Ev    schedule 17.05.2011

Не из коробки. Вам нужно будет вставить код в каждый установщик и получатель свойств либо вручную, либо автоматически, используя переписывание IL.

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

person Daniel Hilgarth    schedule 17.05.2011

Да, конечно...

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

пример:

private string firstName;

public string FirstName
{
 get { return firstName; }

 set { doMethod(); firstName = value;}
}
person Quagmire    schedule 17.05.2011

Это могут делать фиктивные фреймворки, а также библиотеки IoC, такие как Unity. Единственным другим способом сделать это было бы использовать IL-перезапись (как упоминалось ранее).

person Eric Falsken    schedule 17.05.2011

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

public class Person
{
    private string _FirstName;
    public string FirstName 
    { 
        get
        {
            return _FirstName;
        }
        set
        {
            SomeMethod();
            _FirstName = value;
        }
    }
    private void SomeMethod()
    {
        //do something
    }

}
person Jeff    schedule 17.05.2011

Насколько я знаю, вы должны использовать резервное поле и поместить вызов другого метода внутри установщика таким образом:

public class Person {
    private string firstName;
    private string lastName;

    public string FirstName {
        set { 
            DoSomeStuff();
            firstName = value;
        }
        get { return firstName; }
    }

    public string LastName { 
        set {
            DoSomeStuff();
            lastName = value;
        }
        get { return lastName; }
    }
}
person Nathan Craddock    schedule 17.05.2011