【问题标题】:How to override get accessor of a dynamic object's property如何覆盖动态对象属性的获取访问器
【发布时间】:2015-07-07 12:45:36
【问题描述】:

假设我有以下课程:

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

以下块:

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

将返回Matt Smith

让我们将Person 类型更改为动态ExpandoObject

代码如下所示:

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

但这就是我卡住的地方。如何覆盖新 FullName 属性的 get 访问器?

我可以创建一个新方法来达到同样的效果:

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

但这最终会是一个方法而不是一个属性,因此这样称呼它:

person.GetFullName();

编辑

请注意,我不想知道如何定义或创建新的动态属性。我想知道如何覆盖或定义动态属性的 get 访问器。

我想象的代码可以是这样的:

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

然后,像这样调用:

Console.WriteLine(person.FullName); //Prints out "Matt Smith"

【问题讨论】:

  • @MethodMan 我已经看过了,但我无法找到解决方案。请指导一下好吗?
  • @Clint 我不是在问如何动态添加属性。我在问如何覆盖动态添加的属性的获取访问器行为
  • @MatiCicero 这可以通过从 DynamicObject 派生来完成,并显示在该问题的答案中。

标签: c# .net c#-4.0 dynamic


【解决方案1】:
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);
}

【讨论】:

  • 我喜欢这个!但是,如何将 Getter Setter 添加到同一属性?
  • @Mati:你需要什么样的二传手?您的意思是类似于 getter 的程序化程序吗? (例如,当将FullName 属性设置为“Matt Smith”时,setter 可能会分解它并将Name 设置为“Matt”,将Surname 设置为“Smith”。)
【解决方案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; 
        }));

【讨论】:

  • 这个不错!谢谢
  • 谢谢。我在 stackoverflow 写入模式上的第一步:)
【解决方案3】:

试试看

 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);
}

【讨论】:

  • 我喜欢这种方法!我会尽快尝试
【解决方案4】:
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;
    }
}

【讨论】:

  • FullName 是一个常量字符串。我必须将该属性命名为Fullname,否则它将不起作用。我正在寻找更灵活的东西
  • '动态sampleObject = new SampleDynamicObject(); sampleObject.Name = "马特"; sampleObject.Surname = "史密斯"; Console.WriteLine(sampleObject.Fullname);'
  • @DavidG 它可能工作正常,但不能满足我的期望。我想从我的外部代码而不是 DynamicObject 内部定义 get 访问器。这将导致我为不同的获取行为创建不同的 DynamicObject 类型。
猜你喜欢
  • 1970-01-01
  • 2020-07-24
  • 2011-06-15
  • 1970-01-01
  • 2019-12-06
  • 2020-04-18
  • 2010-11-21
  • 2011-08-10
相关资源
最近更新 更多