Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Call a method whenever a property is called

Tags:

Is there a way to add a attribute or something to a class that whenever a property of that class gets called that a method will be called?

Basically i have classes that contains a datarow and instead of accessing the individual column like:(DataRow["some_column_name"]) I want to access them using properties instead like: (MyClass.some_column_name).

My current work around is using reflection to dynamically pass the columns name as parameters like so:

public string some_column_name
    {
        get
        {

            return (string)GetValue(MethodBase.GetCurrentMethod().Name);
        }
        set
        {
            SetValue(MethodBase.GetCurrentMethod().Name, value);
        }
    }

The problem with the above mentioned way is if i have a class with 10 properties inside of it all these properties will have the above code snippet in them bloating the class.

Below code sample contains the get and set value method of the above code snippet these methods would be in the base class:

private DataRow some_table;

protected object GetValue(string columnName)
{
    return some_table[columnName.Substring(columnName.IndexOf('_') + 1)];
}

protected object SetValue(string columnName, object value)
{
    return  some_table[columnName.Substring(columnName.IndexOf('_') + 1)] = value;
}
like image 307
Gys Rademeyer Avatar asked May 07 '18 05:05

Gys Rademeyer


1 Answers

You can use [CallerMemberName] attribute. If you decorate method parameter with this attribute like this:

protected object GetValue([CallerMemberName] string columnName = null)
{
    return some_table[columnName.Substring(columnName.IndexOf('_') + 1)];
}

protected object SetValue(object value, [CallerMemberName] string columnName = null)
{
    return  some_table[columnName.Substring(columnName.IndexOf('_') + 1)] = value;
}

Compiler will use, well, caller member (method) name instead of default null value. So your property becomes just:

public string some_column_name
{
    get
    {
        // current method name ("some_column_name") will be passed 
        // as "column" parameter
        return (string)GetValue();
    }
    set
    {
        // same here
        SetValue(value);
    }
}

Another alternative might be to inherit from DynamicObject:

public class DynamicRow : DynamicObject {
    public override bool TryGetMember(GetMemberBinder binder, out object result) {
        // this will be called when you access dynamic property
        // like x.some_column_name
        result = GetValue(binder.Name);
        return true;
    }

    public override bool TrySetMember(SetMemberBinder binder, object value) {
        // this will be called when you assign dynamic property
        // like x.some_column_name = "some value"
        SetValue(binder.Name, value);
        return true;
    }

    private DataRow some_table;

    protected object GetValue(string columnName) {
        return some_table[columnName.Substring(columnName.IndexOf('_') + 1)];
    }

    protected object SetValue(string columnName, object value) {
        return some_table[columnName.Substring(columnName.IndexOf('_') + 1)] = value;
    }
}

Then you can use it like this:

dynamic x = new DynamicRow();
object val = x.some_column_name;
x.some_column_name = "some value";

But I personally don't quite like it, because you are losing all type safety (any typo will only be caught at runtime), compared to using static list of properties like in the first approach.

like image 64
Evk Avatar answered Sep 28 '22 19:09

Evk