Generate custom setter using attributes

2019-04-09 08:57发布

问题:

In classes whose instances I persist using an object database, I keep having to do this:

private string _name;
public string Name
    {
    get { return this._name; }
    set { _name = value; this.Save(); }
    }

whereas I would much rather type this:

[PersistedProperty(Name)]
private string _name;

where the PersistedProperty attributes generates a Getter and Setter just like the default [Property()] attribute, except I want to add a line of code to the generated Setter.

Is there a way I can create an attribute which does this? Hopefully , which works with Intellisense.

How does the default [Property()] attribute even do it's stuff? If I saw the code I could graft that...

Note: I am actually doing this in Boo, but thought I'd give c# code as more people might be willing to answer that, however, if there is a Boo specific solution, I'm all ears!

Update:

My aim was simply to reduce typing and clutter. It turns out the simplest way of doing this was with a script which generates partial classes based on markup in my classes.

Auto-generating source code from markup (in tandem with partial classes) is easy, and actually looks like an extremely promising way to get round some of the problems we normally try to solve with inheritance and generic types.

回答1:

This requires aspect oriented programming. While not directly supported in .NET, it can be done via third party tooling, such as PostSharp.

For intellisense to work, however, this must be done in a library, as the (final) compiled code will be unrolled into the full property getter/setter.



回答2:

Not easy to implement using attributes IMO. Maybe you could use another approach, such as an extension method:

// Extension method that allows updating a property
// and calling .Save() in a single line of code.
public static class ISaveableExtensions
{
    public static void UpdateAndSave<T>(
        this ISaveable instance,
        Expression<Func<T>> propertyExpression, T newValue)
    {
        // Gets the property name
        string propertyName = ((MemberExpression)propertyExpression.Body).Member.Name;

        // Updates its value
        PropertyInfo prop = instance.GetType().GetProperty(propertyName);
        prop.SetValue(instance, newValue, null);

        // Now call Save
        instance.Save();
    }
}
...
// Some interface that implements the Save method
public interface ISaveable
{
    void Save();
}
...
// Test class
public class Foo : ISaveable
{
    public string Property { get; set; }

    public void Save()
    {
        // Some stuff here
        Console.WriteLine("Saving");
    }

    public override string ToString()
    {
        return this.Property;
    }
}
...
public class Program
{
    private static void Main(string[] args)
    {
        Foo d = new Foo();

        // Updates the property with a new value, and automatically call Save
        d.UpdateAndSave(() => d.Property, "newValue");

        Console.WriteLine(d);
        Console.ReadKey();
    }
}

It's type-safe, autocompletion-friendly, but it requires more code than just .Save() in all setters, so not sure I would use it actually...