Hatjhie
Hatjhie

Reputation: 1365

.NET Reflection to Identify Property Relationship

supposedly I have the following class below:

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

Let's see the codes below:

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

Now, if we are only given Car instance which is superCar, is it possible to know from Reflection that superCar instance is actually belong to one of the Vehicle's properties?

Ok. Thanks for your comments. I think it's possible through Reflection.

So, if we extend the codes:

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

Provided, we are only given:

PropertyInfo propInfo = props[0];

We could know this propInfo actually belong to which class by using MemberInfo:

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

It would return Vehicle.

Thanks!

Upvotes: 1

Views: 743

Answers (2)

Konrad Kokosa
Konrad Kokosa

Reputation: 16878

Short answer

No, it is not possible. Reflection is a way of reading static metadata of the assembly and types within. Information about type properties is an example of such metadata. But information like - "which objects are referencing some object" - is not a static metadata. It is a runtime data and in general it is not accessible in .NET. Other thing is, it really doesn't have to. There is always better way to design things (like references pointing its parents). In other words, you really never and ever should need to do that so better remember that it is not possible.

Long answer

Saying above, I would like to point out that surprisingly, what are you trying to do is possible with help of astonishing Microsoft.Diagnostics.Runtime library. As authors says:

ClrMD is a set of advanced APIs for programmatically inspecting a crash dump of a .NET program much in the same way as the SOS Debugging Extensions (SOS). It allows you to write automated crash analysis for your applications and automate many common debugger tasks.

In fact, you can also attach to self process and analyze it during normal work. Below code is quite hacky and should be never used probably, but it works and shows how powerful is .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;
}

then for simple test:

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

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

Note: For attaching to itself, only AttachFlag.Passive is possible which is described as:

Performs a "passive" attach, meaning no debugger is actually attached to the target process. The process is not paused, so queries for quickly changing data (such as the contents of the GC heap or callstacks) will be highly inconsistent** unless the user pauses the process through other means.

so results may be not always deterministic.

Upvotes: 3

tpichler
tpichler

Reputation: 46

No, not really, but you can find out if your object reference is actually pointing to the same object.

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

Upvotes: 2

Related Questions