4

Let's suppose I have the following class:

public class Person {
    public string Name { get; set; }
    public string Surname { get; set; }
    public string FullName {
        get {
            return Name + " " + Surname;
        }
    }
}

The following block:

Person person = new Person();
person.Name = "Matt";
person.Surname = "Smith";
return person.FullName;

would return Matt Smith.

Let's change our Person type to a dynamic ExpandoObject.

The code would look like this:

dynamic person = new ExpandoObject();
person.Name = "Matt";
person.Surname = "Smith";

But here's where I am stuck. How can I override the get accessor of a new FullName property?

I could achieve the same effect creating a new method:

person.GetFullName = (Func<string>)(() => {
      return person.Name + " " + person.Surname;
});

But this would end up with a method and not a property, therefore calling it like:

person.GetFullName();

EDIT

Please note that I do not want to know how to define or create a new dynamic property. I would like to know how to override or define a get accessor for a dynamic property.

I picture the code can be something like this:

person.FullName.get = (Func<string>)(() => {
     return person.Name + " " + person.Surname;
});

Then, invoked like this:

Console.WriteLine(person.FullName); //Prints out "Matt Smith"
Matias Cicero
  • 25,439
  • 13
  • 82
  • 154
  • 1
    have you looked at [MSDN Dynamic Object Class](https://msdn.microsoft.com/en-us/library/system.dynamic.dynamicobject%28v=vs.110%29.aspx) – MethodMan Apr 28 '15 at 15:22
  • @MethodMan I have looked at it, but I can't manage to find a solution. Can you guide me, please? – Matias Cicero Apr 28 '15 at 15:27
  • possible duplicate of [Dynamically Add C# Properties at Runtime](http://stackoverflow.com/questions/15819720/dynamically-add-c-sharp-properties-at-runtime) – Clint Apr 28 '15 at 15:43
  • @Clint I'm not asking how to add a property dynamically. I'm asking how to override a dynamically added property's get accessor behaviour – Matias Cicero Apr 28 '15 at 15:44
  • 1
    @MatiCicero which can be done by deriving from DynamicObject and is shown in the answer for that question. – Clint Apr 28 '15 at 15:45
  • @Clint In that answer, my property must be called `Fullname` or else it wouldn't work. I'm looking for something more flexible – Matias Cicero Apr 28 '15 at 15:48
  • @Clint Also, if I rename my `Name` or `Surname` properties, the code breaks. – Matias Cicero Apr 28 '15 at 15:49
  • @MatiCicero what sort of flexibility are you looking for exactly? You may want to edit your question to indicate what it is you're trying to achieve explicitly. – Clint Apr 28 '15 at 15:49
  • @MatiCicero that's just part and parcel of how dynamic objects work unfortunately. – Clint Apr 28 '15 at 15:50
  • @Clint Something like my example of `GetFullName`, but instead of defining a method, I want to define a get accessor. Maybe there is a way using lambdas? – Matias Cicero Apr 28 '15 at 15:50

4 Answers4

8
dynamic person = new GetterExpando();
person.Name = "Matt";
person.Surname = "Smith";
person.FullName = new GetterExpando.Getter(x => x.Name + " " + x.Surname);

Console.WriteLine(person.FullName);  // Matt Smith

// ...

public sealed class GetterExpando : DynamicObject
{
    private readonly Dictionary<string, object> _data = new Dictionary<string, object>();

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        _data[binder.Name] = value;
        return true;
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        object value;
        if (_data.TryGetValue(binder.Name, out value))
        {
            var getter = value as Getter;
            result = (getter == null) ? value : getter(this);
            return true;
        }
        return base.TryGetMember(binder, out result);
    }

    public delegate object Getter(dynamic target);
}
LukeH
  • 263,068
  • 57
  • 365
  • 409
  • I like this one! How would I add a Getter **and** a Setter to the same property, though? – Matias Cicero Apr 28 '15 at 18:13
  • @Mati: What sort of setter do you need? Do you mean something programmatic similar to the getter? (For example, when setting the `FullName` property to "Matt Smith", the setter might decompose this and set `Name` to "Matt" and `Surname` to "Smith".) – LukeH Apr 29 '15 at 14:03
2
public class SampleDynamicObject : DynamicObject
{
    Dictionary<string, Func<dynamic, object>> customFieldHandlers = new Dictionary<string, Func<dynamic, object>>();

    Dictionary<string, object> values = new Dictionary<string, object>();

    public void Get(string property, Func<dynamic, object> handler)
    {
        customFieldHandlers.Add(property, handler);
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        if (customFieldHandlers.ContainsKey(binder.Name))
        {
            result = customFieldHandlers[binder.Name](this);
            return true;
        }

        result = values[binder.Name];
        return true;
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        values[binder.Name] = value;
        return true;
    }
}


dynamic sampleObject = new SampleDynamicObject();

        sampleObject.Get("FullName", (Func<dynamic, object>)((o) => 
        {
            dynamic obj = o;
            return o.Name + " " + o.Surname; 
        }));
1

Try it

 public class MyDynamic : DynamicObject
{
    private Dictionary<string,object> obj = new Dictionary<string, object>();
    public Func<string,dynamic,object> PropertyResolver { get; set; }
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        if (obj.ContainsKey(binder.Name))
        {
            result =  obj[binder.Name];
            return true;
        }

        if (PropertyResolver != null)
        {
            var actResult = PropertyResolver(binder.Name, this);
            result = actResult;
            return true;
        }

        return base.TryGetMember(binder, out result);
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        obj[binder.Name] = value;
        return true;
    }
}

 static void Main(string[] args)
    {
        dynamic person = new MyDynamic();
        person.Name = "Matt";
        person.Surname = "Smith";
        person.PropertyResolver = (Func<string, dynamic, object>)
            ((string name, dynamic me) => { return name == "FullName" ? me.Name + me.Surname : "";  });
Console.WriteLine(person.FullName);
}
nhabuiduc
  • 998
  • 7
  • 8
0
public class SampleDynamicObject : DynamicObject
{
    Dictionary<string, object> values = new Dictionary<string, object>();

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        if (binder.Name == "Fullname")
        {
            result = values["Name"] + " " + values["Surname"];
            return true;
        }

        result = values[binder.Name];

        return true;
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        values[binder.Name] = value;
        return true;
    }
}
VladL
  • 12,769
  • 10
  • 63
  • 83
  • `FullName` is a constant string. I must name the property `Fullname` or else it wouldn't work. I'm searching for something more flexible – Matias Cicero Apr 28 '15 at 15:46
  • 'dynamic sampleObject = new SampleDynamicObject(); sampleObject.Name = "Matt"; sampleObject.Surname = "Smith"; Console.WriteLine(sampleObject.Fullname);' – Vasil Akhmetov Apr 28 '15 at 15:47
  • @DavidG It may work fine, but it does not satisfy my expectations. I would like to define the get accessor from my outside code, not inside the DynamicObject. This would lead me to create different `DynamicObject` types for different get behaviours. – Matias Cicero Apr 28 '15 at 16:03