【问题标题】:.NET Reflection to Identify Property Relationship.NET 反射识别属性关系
【发布时间】:2014-02-14 09:16:03
【问题描述】:

假设我有以下课程:

public class Car
{
    public Car()
    {

    }
}

public class Motor
{
    public Motor()
    {

    }
}

public class Vehicle
{
    public Vehicle()
    {
        SuperCar = new Car();
    }

    public Car SuperCar { get; set; }
    public string TestName { get; set; }
}

让我们看看下面的代码:

 Vehicle vehicle = new Vehicle();
 Car superCar = vehicle.SuperCar;

现在,如果我们只给定 Car 实例,它是 superCar,是否有可能从反射中知道 superCar 实例实际上属于 Vehicle 的属性之一?

好的。谢谢你的cmets。我认为可以通过反射实现。

所以,如果我们扩展代码:

PropertyInfo[] props = vehicle.GetType().GetProperties();

提供,我们只得到:

PropertyInfo propInfo = props[0];

我们可以通过MemberInfo知道这个propInfo实际上属于哪个类:

Console.WriteLine(((System.Reflection.MemberInfo)(propInfo)).DeclaringType.Name);

它会返回 Vehicle。

谢谢!

【问题讨论】:

  • 我认为您不需要对此进行反思。我想你只需要if (superCar == vehicle.superCar)
  • 如果你只有一个Car的实例,就没有办法从那里遍历属性到达Vehicle。因此,除非您已经从可以交叉引用的 Vehicle 列表开始,否则反射对您没有帮助。
  • 问题是,你为什么要这么做?这不是精心设计的面向对象程序中需要的信息。

标签: c# .net reflection properties relationship


【解决方案1】:

简答

不,这是不可能的。反射是一种读取静态元数据的程序集和其中的类型的方法。有关类型属性的信息就是此类元数据的一个示例。但是诸如“哪些对象正在引用某个对象”之类的信息不是静态元数据。它是一个运行时数据,通常无法在 .NET 中访问。另一件事是,它真的不必。总是有更好的方法来设计事物(例如指向其父母的参考)。换句话说,你真的永远不需要这样做,所以最好记住这是不可能的。

长答案

如上所述,我想指出令人惊讶的是,借助令人惊讶的 Microsoft.Diagnostics.Runtime 库,您尝试做的事情可能。正如作者所说:

ClrMD 是一组高级 API,用于以与 SOS 调试扩展 (SOS) 相同的方式以编程方式检查 .NET 程序的故障转储。它允许您为您的应用程序编写自动崩溃分析并自动执行许多常见的调试器任务。

其实你也可以在正常工作的时候附加到自己的进程中去分析。下面的代码非常老套,可能永远不应该使用,但它有效并显示了 .NET 世界的强大:

public bool IsReferencedByAnyVehicle(Car car)
{
    ulong ptr;
    // Nasty way of getting address 
    unsafe
    {
        TypedReference tr = __makeref(car);
        ptr = (ulong)(**(IntPtr**)(&tr));
        Console.WriteLine(ptr);
    }

    // Attach to the process itself, hence only AttachFlag.Passive flag is possible
    var process = Process.GetCurrentProcess();
    using (var dataTarget = DataTarget.AttachToProcess(process.Id, 250, AttachFlag.Passive))
    {
        string dacLocation = dataTarget.ClrVersions[0].TryGetDacLocation();
        ClrRuntime runtime = dataTarget.CreateRuntime(dacLocation);

        ClrHeap heap = runtime.GetHeap();
        // Get all Vehicle objects from heap that has reference to ptr
        var refs = heap.EnumerateObjects()
            .Select(obj => new
            {
                Type = heap.GetObjectType(obj),
                ObjectAddress = obj
            })
            .Where(t => t.Type != null &&
                        t.Type.Name.EndsWith("Vehicle") &&
                        GetReferences(t.Type, t.ObjectAddress).Contains(ptr))
            .Any();
        // Cleanup
        runtime.Flush();
        return refs;
    }
}

public List<ulong> GetReferences(ClrType type, ulong objRef)
{
    var result = new List<ulong>();
    type.EnumerateRefsOfObjectCarefully(objRef, (addr, _) => result.Add(addr));
    return result;
}

然后进行简单测试:

Vehicle vehicle = new Vehicle();
Car superCar = vehicle.SuperCar;
Car localCar = new Car();

bool isSuperCar = IsReferencedByAnyVehicle(superCar); // true
bool isLocalCar = IsReferencedByAnyVehicle(localCar); // false

注意:对于附加到自身,只有 AttachFlag.Passive 是可能的,描述为:

执行“被动”附加,这意味着实际上没有调试器附加到目标进程。进程没有暂停,所以对于快速变化的数据(比如 GC 堆或调用栈的内容)的查询将高度不一致**,除非用户通过其他方式暂停进程。

因此结果可能并不总是确定的。

【讨论】:

  • 感谢您的努力和解释。
【解决方案2】:

不,不是,但您可以确定您的对象引用是否实际上指向同一个对象。

        if (Object.ReferenceEquals(superCar, vehicle.SuperCar))
        {
            System.Diagnostics.Debug.WriteLine("Yes it is");
        }

【讨论】:

  • 或者,你可以,你知道,if (superCar == vehicle.superCar)here
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-22
  • 2012-04-02
  • 2012-01-23
  • 2010-10-20
相关资源
最近更新 更多