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。感谢您的努力和解释。感谢您的努力和解释。