我如何获得一个允许我分辨一个类的差异实例的ID?
想象一下,我有一个类,有两个实例:
MyClass a = new MyClass(); MyClass b = new MyClass();
MyClass有一个PrintUniqueInstanceID方法:
void PrintUniqueInstanceID() { Console.Write("Unique ID for the *instance* of this class: {0}", [what goes here???] ); }
理想情况下,输出将是这样的:
Unique ID for the *instance* of this class: 23439434 // from a.PrintUniqueInstanceID Unique ID for the *instance* of this class: 89654 // from b.PrintUniqueInstanceID
那么 – 我将在上面的“ [what goes here???]
”中插入[what goes here???]
,它会为每个类的唯一实例打印一个唯一的数字?
思路
- 也许将“this”转换为int指针,并使用它?
- 以某种方式使用GCHandle?
- 在方法中访问“this”的属性,以唯一标识它?
(可选)专家背景资料
我需要这个的原因是我使用AOP和PostSharp来自动检测线程问题。 我需要在字典中查找类的每个唯一实例,以validation多个线程没有访问类的同一个唯一实例(如果每个类实例有一个线程,则确定)。
更新
正如其他人所指出的,我应该提到我不能触及30,000线项目中的任何现有类。 上面的PrintUniqueInstanceID是一个方面(参见PostSharp ),它被添加到顶级类,由整个项目中的每个类inheritance,并在整个项目中的每个方法条目上执行。
一旦我确认一切都是线程安全的,我将删除方面以恢复性能。
将Guid属性添加到您的类,然后在类的构造函数中将其分配给NewGuid()。
public class MyClass { public Guid InstanceID {get; private set;} // Other properties, etc. public MyClass() { this.InstanceID = Guid.NewGuid(); } void PrintUniqueInstanceID() { Console.Write("Unique ID for the *instance* of this class: {0}", this.InstanceID); } }
修改后的答案
根据我们现有的其他信息,我相信您可以使用ConditionalWeakTable
( 仅从.NET 4开始 )轻松解决您的问题。
因此,您可以在“manager”类中创建这样一个全局表,并将每个对象与long
, Guid
或您可能想要的任何其他内容相关联。 每当您的经理遇到一个对象时,它就可以获得其关联的ID(如果您之前已经看过它)或将其添加到表中并将其与当场创建的新ID相关联。
¹ 实际上表中的值必须是引用类型,因此您不能直接使用例如long
值。 但是,一个简单的解决方法是使用object
而不是将long
值设置为boxed。
原始答案
这不是static
成员的基本用法示例吗?
class Foo { private static int instanceCounter; private readonly int instanceId; Foo() { this.instanceId = ++instanceCounter; } public int UniqueId { get { return this.instanceId; } } }
当然,您必须注意标识符的范围,以便在创建数十亿个实例时不开始重用它们,但这很容易解决。
使用ObjectIDGenerator类:
https://msdn.microsoft.com/en-us/library/system.runtime.serialization.objectidgenerator.aspx
引用:
这些ID在ObjectIDGenerator实例的生命周期中是唯一的。
使用哈希表,ObjectIDGenerator保留将哪个ID分配给哪个对象。 唯一标识每个对象的对象引用是运行时垃圾收集堆中的地址。 对象引用值可以在序列化期间更改,但表会自动更新,以便信息正确。
对象ID是64位数字。 分配从一开始,因此零永远不是有效的对象ID。 格式化程序可以选择零值来表示值为null的对象引用。
更新
这是解决问题的代码。 在方面类中,使用以下内容:
public static ObjectIDGenerator ObjectIDGen = new ObjectIDGenerator();
然后:
bool firstTime; long classInstanceID = ObjectIDGenerator.GetId(args.Instance, out firstTime);
更新
以为我会发布整个post所基于的代码。 此代码通过在多个线程访问同一个类实例时触发警告,帮助检测整个项目中的线程安全热点。
如果您有30k行现有代码,并且想要添加更正式的线程安全validation(这是非常难以正常执行的操作),这很有用。 它确实会影响运行时性能,因此您可以在调试模式下运行它几天后将其删除。
要使用,请将PostSharp +此类添加到项目中,然后将“[MyThreadSafety]”方面添加到任何类。 PostSharp将在每次方法调用之前在“OnEntry”中插入代码。 方面传播到所有子类和子方法,因此您可以使用一行代码将线程安全检查添加到整个项目。
有关此技术的另一个示例,请参阅设计用于轻松向方法调用添加缓存的示例 。
using System; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Serialization; using System.Text; using System.Threading; using MyLogType; using PostSharp.Aspects; using System.Collections.Concurrent; using PostSharp.Extensibility; namespace Demo { /// /// Example code based on the page from a Google search of: /// postsharp "Example: Tracing Method Execution" /// [Serializable] public sealed class MyThreadSafetyCheck : OnMethodBoundaryAspect { /// /// We need to be able to track if a different ThreadID is seen when executing a method within the *same* instance of a class. Its /// ok if we see different ThreadID values when accessing different instances of a class. In fact, creating one copy of a class per /// thread is a reliable method to fix threading issues in the first place. /// /// Key: unique ID for every instance of every class. /// Value: LastThreadID, tracks the ID of the last thread which accessed the current instance of this class. /// public static ConcurrentDictionary DetectThreadingIssues = new ConcurrentDictionary(); /// /// Allows us to generate a unique ID for each instance of every class that we see. /// public static ObjectIDGenerator ObjectIDGenerator = new ObjectIDGenerator(); /// /// These fields are initialized at runtime. They do not need to be serialized. /// [NonSerialized] private string MethodName; [NonSerialized] private long LastTotalMilliseconds; /// /// Stopwatch which we can use to avoid swamping the log with too many messages for threading violations. /// [NonSerialized] private Stopwatch sw; /// /// Invoked only once at runtime from the static constructor of type declaring the target method. /// /// public override void RuntimeInitialize(MethodBase method) { if (method.DeclaringType != null) { this.MethodName = method.DeclaringType.FullName + "." + method.Name; } this.sw = new Stopwatch(); this.sw.Start(); this.LastTotalMilliseconds = -1000000; } /// /// Invoked at runtime before that target method is invoked. /// /// Arguments to the function. public override void OnEntry(MethodExecutionArgs args) { if (args.Instance == null) { return; } if (this.MethodName.Contains(".ctor")) { // Ignore the thread that accesses the constructor. // If we remove this check, then we get a false positive. return; } bool firstTime; long classInstanceID = ObjectIDGenerator.GetId(args.Instance, out firstTime); if (firstTime) { // This the first time we have called this, there is no LastThreadID. Return. if (DetectThreadingIssues.TryAdd(classInstanceID, Thread.CurrentThread.ManagedThreadId) == false) { Console.Write(string.Format("{0}Error E20120320-1349. Could not add an initial key to the "DetectThreadingIssues" dictionary.n", MyLog.NPrefix())); } return; } int lastThreadID = DetectThreadingIssues[classInstanceID]; // Check 1: Continue if this instance of the class was accessed by a different thread (which is definitely bad). if (lastThreadID != Thread.CurrentThread.ManagedThreadId) { // Check 2: Are we printing more than one message per second? if ((sw.ElapsedMilliseconds - this.LastTotalMilliseconds) > 1000) { Console.Write(string.Format("{0}Warning: ThreadID {1} then {2} accessed "{3}". To remove warning, manually check thread safety, then add "[MyThreadSafetyCheck(AttributeExclude = true)]".n", MyLog.NPrefix(), lastThreadID, Thread.CurrentThread.ManagedThreadId, this.MethodName)); this.LastTotalMilliseconds = sw.ElapsedMilliseconds; } } // Update the value of "LastThreadID" for this particular instance of the class. DetectThreadingIssues[classInstanceID] = Thread.CurrentThread.ManagedThreadId; } } }
我可以按需提供完整的演示项目。
您不能从基类或接口inheritance所有类,并且需要实现UniqueID属性?
另一种可能性是将它们包装在一个包含通用对象引用和唯一ID的类中,然后按照懒惰的方式对它们进行编目。 清理这样一个独特任务目录可能很尴尬。
可以使用:
ClassName + MethodName + this.GetHashCode();
尽管GetHashCode()不保证唯一值,但如果它与类名和方法名配对,则冲突的可能性会降低。
即使存在冲突,唯一的影响是它会在日志中生成更多警告,这不是什么大问题。
上述就是C#学习教程:我如何获得一个允许我分辨一个类的差异实例的ID?分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—计算机技术网(www.ctvol.com)!
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/cdevelopment/1017124.html