C# .NET反射以识别属性关系

C# .NET反射以识别属性关系,c#,.net,reflection,properties,relationship,C#,.net,Reflection,Properties,Relationship,假设我有以下课程: public class Car { public Car() { } } public class Motor { public Motor() { } } public class Vehicle { public Vehicle() { SuperCar = new Car(); } public Car SuperCar { get; set; } pub

假设我有以下课程:

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;
现在,如果我们只给出了一个汽车实例,它是超级汽车,那么从反射中可以知道超级汽车实例实际上属于汽车的一个属性吗

嗯。谢谢你的评论。我认为通过思考是可能的

因此,如果我们扩展代码:

PropertyInfo[] props = vehicle.GetType().GetProperties();
前提是,我们只得到:

PropertyInfo propInfo = props[0];
通过使用MemberInfo,我们可以知道此propInfo实际上属于哪个类:

Console.WriteLine(((System.Reflection.MemberInfo)(propInfo)).DeclaringType.Name);
它将返回车辆


谢谢

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

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

不,不是真的,但是你可以发现你的对象引用是否真的指向同一个对象

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

简短回答

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

长答案

如上所述,我想指出,令人惊讶的是,在惊人的图书馆的帮助下,您试图做的事情是可能的。正如作者所说:

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

事实上,您还可以在正常工作期间附加到自我过程并对其进行分析。下面的代码非常粗糙,可能永远都不应该使用,但它运行良好,显示了.NET world的强大功能:

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;
}
注意:对于附加到自身,只能使用AttachFlag.Passive,如下所述:

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


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

简短回答

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

长答案

如上所述,我想指出,令人惊讶的是,在惊人的图书馆的帮助下,您试图做的事情是可能的。正如作者所说:

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

事实上,您还可以在正常工作期间附加到自我过程并对其进行分析。下面的代码非常粗糙,可能永远都不应该使用,但它运行良好,显示了.NET world的强大功能:

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;
}
注意:对于附加到自身,只能使用AttachFlag.Passive,如下所述:

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


所以结果可能并不总是确定的。

我认为你不需要对此进行反思。我认为您只需要
if(superCar==vehicle.superCar)
如果您只有
Car
的一个实例,就无法从那里遍历属性并到达
vehicle
。因此,除非您已经从一个可以交叉引用的
Vehicle
列表开始,否则反思不会对您有所帮助。问题是,您为什么要这样做?这不是设计良好的面向对象程序所需要的信息。我认为您不需要对此进行反思。我认为您只需要
if(superCar==vehicle.superCar)
如果您只有
Car
的一个实例,就无法从那里遍历属性并到达
vehicle
。因此,除非您已经从一个可以交叉引用的
Vehicle
列表开始,否则反思不会对您有所帮助。问题是,您为什么要这样做?这不是设计良好的面向对象程序所需的信息。或者,您可以,您知道,
if(superCar==vehicle.superCar)
See。或者,您可以,您知道,
if(superCar==vehicle.superCar)
See。感谢您的努力和解释。感谢您的努力和解释。