如何使用动态找出何时使用 属性?

How can I use a dynamic to find out when a property is used?

我想找出某个方法使用了源输入对象中的哪些属性。执行该方法后,我需要在数据库中存储使用了哪些属性。

输入可以是任何具有简单类型的 class,如下所示:

public class MyData : IMyData
{
    public string A { get; set; }
    public int B { get; set; }
    public decimal C { get; set; }
}

我认为可以使用接口作为方法的输入来完成,因此我可以用更高级的对象替换原始对象,该对象存储属性的用法

public interface IMyData
{
    string A { get; }
    int B { get; }
    decimal C { get; }
}

那我可以

  1. 创建具有相同属性的动态对象
  2. 使用ImpromptuInterface模拟动态对象实现我的接口
  3. 用这个动态接口调用我的方法

    private static void Main()
    {
        var data = new MyData { A = "Test", B = 3, C = new decimal(1.2) };
    
        IDictionary<string, object> replacementObject = new ExpandoObject();
        replacementObject.Add("FieldsUsed", new List<string>());
        foreach (var property in data.GetType().GetProperties())
            replacementObject.Add(property.Name, property.GetValue(data));
    
        var replacementInterface = replacementObject.ActLike<IMyData>();
    
        DoStuff(replacementInterface);
        Console.WriteLine($"The method used these fields {string.Join(", ", (List<string>)replacementObject["FieldsUsed"])}");
    }
    
    private static void DoStuff(IMyData source)
    {
        Console.WriteLine($"A is {source.A}");
        if (source.B > 5)
            Console.WriteLine($"C is {source.C}");
    }
    

在上面的示例中,我想存储字段 A 和 B 已被使用。

当我的 DoStuff 方法使用 属性 时,只有我不知道应该如何存储。

如果您想知道何时使用 属性,像 INotifyPropertyChanged 这样的接口可以在运行时为您完成。这个例子只是关于写入通知(实际上改变了一个值),但是将它扩展到读取和写入是微不足道的。这当然不是一件完美的事情,因为不同的执行可能遵循使用不同属性的不同代码路径。

如果函数采用特定类型作为输入,您必须假设所有属性都可能相关。对于抽象类型和接口尤其如此——通常接口 exists 这个函数。如果它是这两者之一,您也可以始终提供自己对这些接口和抽象的实现 class.

我无法摆脱这是一个 XY 问题的感觉。

你可以这样写一个包装器:

public class ClassWrapper<T>: DynamicObject where T:class
{
    private readonly T _obj;
    private readonly List<string> _fieldsUsed=new List<string>();
    public ClassWrapper(T obj)
    {
        _obj = obj;
    }
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        PropertyInfo propertyInfo = _obj.GetType().GetProperty(binder.Name);
        _fieldsUsed.Add(binder.Name);
        result = propertyInfo.GetValue(_obj);
        return true;
    }
    public List<string> GetFieldsUsed() => _fieldsUsed;

    public T GetWrapper()
    {
        return this.ActLike<T>();
    }
}

并像

一样使用它
 var data = new MyData { A = "Test", B = 3, C = new decimal(1.2) };
 var mc=new ClassWrapper<IMyData>(data);

 IMyData wrapped = mc.GetWrapper();

 DoStuff(wrapped);
 Console.WriteLine($"The method used these fields {string.Join(", ", (List<string>)mc.GetFieldsUsed())}");