如何获得一个ID,该ID可以区分一个类的不同实例? [英] How do I obtain an ID that allows me to tell difference instances of a class apart?

查看:101
本文介绍了如何获得一个ID,该ID可以区分一个类的不同实例?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

想象一下我有一个类,有两个实例:

Imagine I have a single class, with two instances:

MyClass a = new MyClass();
MyClass b = new MyClass();

MyClass具有方法PrintUniqueInstanceID:

MyClass has a method PrintUniqueInstanceID:

void PrintUniqueInstanceID()
{
  Console.Write("Unique ID for the *instance* of this class: {0}", 
      [what goes here???]
  );
}

理想情况下,输出将类似于:

Ideally, the output would be something like:

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???]"中插入什么,为类的每个唯一实例打印一个唯一编号?

So - what would I insert in "[what goes here???]", above, which prints a unique number for every unique instance of the class?

想法

  1. 也许将"this"强制转换为int指针,并使用它?
  2. 以某种方式使用GCHandle?
  3. 在方法内访问"this"属性以唯一地标识它吗?

(可选)专家背景信息

我需要这个的原因是我正在使用AOP和PostSharp自动检测线程问题.我需要在字典中查找该类的每个唯一实例,以验证多个线程没有访问一个类的同一唯一实例(如果每个类实例有一个线程,则可以).

The reason I need this is that I am using AOP and PostSharp to automatically detect threading issues. I need to look up each unique instance of the class in a dictionary, in order to verify that multiple threads are not accessing the same unique instance of a class (its ok if there is one thread per class instance).

更新

正如其他人指出的那样,我应该提到,我无法触及30,000行项目中的任何现有类.上面的PrintUniqueInstanceID是一个方面(请参见 PostSharp ),已被添加到顶层类,并且被整个项目中的每个类继承,并在整个项目中的每个方法条目上执行.

As others have pointed out, I should mention that I can't touch any of the existing classes in the 30,000 line project. PrintUniqueInstanceID, above, is an aspect (see PostSharp) that is added to the top level class, is inherited by every class in the entire project, and executes on every method entry in the entire project.

一旦我确认所有内容都是线程安全的,我将删除该方面以恢复性能.

Once I have verified that everything is thread safe, I'll remove the aspect to restore performance.

推荐答案

使用ObjectIDGenerator类:

Use ObjectIDGenerator class:

http://msdn.microsoft.com/zh-CN/library/system.runtime.serialization.objectidgenerator.aspx

报价:

这些ID在ObjectIDGenerator实例的生存期内是唯一的.

The IDs are unique for the life of the ObjectIDGenerator instance.

使用哈希表,ObjectIDGenerator保留分配了哪些ID 到哪个对象.对象引用,它们唯一地标识每个对象 对象,是运行时垃圾收集堆中的地址.目的 参考值可以在序列化过程中更改,但是该表是 自动更新,因此信息正确无误.

Using a hash table, the ObjectIDGenerator retains which ID is assigned to which object. The object references, which uniquely identify each object, are addresses in the runtime garbage-collected heap. Object reference values can change during serialization, but the table is updated automatically so the information is correct.

对象ID是64位数字.分配从1开始,所以零是 永远不会是有效的对象ID.格式化程序可以选择零值来 表示对象引用,其值为空.

Object IDs are 64-bit numbers. Allocation starts from one, so zero is never a valid object ID. A formatter can choose a zero value to represent an object reference whose value is null.

更新

这是解决问题的代码.在方面类中,使用以下内容:

This is the code that solves the problem. In the aspect class, use the following:

public static ObjectIDGenerator ObjectIDGen = new ObjectIDGenerator();

然后:

bool firstTime;
long classInstanceID = ObjectIDGenerator.GetId(args.Instance, out firstTime);

更新

以为我会发布整个帖子所基于的代码.如果多个线程访问一个类的同一实例,则通过触发警告,此代码有助于检测整个项目中的线程安全热点.

Thought I'd post the code that this entire post is based on. This code helps to detect thread safety hotspots across an entire project, by triggering warnings if multiple threads access the same instance of a class.

如果您有3万行现有代码,并且想要添加更正式的线程安全性验证(通常很难做到这一点),则很有用.它确实会影响运行时性能,因此您可以在调试模式下运行几天后将其删除.

Useful if you have 30k lines of existing code, and you want to add a more formal verification of thread safety (something which is extremely difficult to do normally). It does impact runtime performance, so you can remove it after running it for a few days in debug mode.

要使用,请将PostSharp +此类添加到您的项目,然后将一个方面"[MyThreadSafety]"添加到任何类. PostSharp将在每次方法调用之前将代码插入"OnEntry"中.该方面会传播到所有子类和子方法,因此您可以只用一行代码就可以对整个项目添加线程安全检查.

To use, add PostSharp + this class to your project, then add an aspect "[MyThreadSafety]" to any class. PostSharp will insert the code in "OnEntry" before every method call. The aspect propagates to all sub-classes and sub-methods, so you can add thread safety checks to an entire project with just one line of code.

有关该技术的另一个示例,请参见旨在轻松添加的示例缓存到方法调用.

For another example of this technique in action, see an example designed to easily add caching to method calls.

    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
    {
        /// <summary>
        /// Example code based on the page from a Google search of:
        /// postsharp "Example: Tracing Method Execution"
        /// </summary>
        [Serializable]
        public sealed class MyThreadSafetyCheck : OnMethodBoundaryAspect
        {
            /// <summary>
            /// 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.
            /// </summary>
            public static ConcurrentDictionary<long, int> DetectThreadingIssues = new ConcurrentDictionary<long, int>();

            /// <summary>
            /// Allows us to generate a unique ID for each instance of every class that we see.
            /// </summary>
            public static ObjectIDGenerator ObjectIDGenerator = new ObjectIDGenerator();

            /// <summary>
            /// These fields are initialized at runtime. They do not need to be serialized.
            /// </summary>
            [NonSerialized]
            private string MethodName;

            [NonSerialized]
            private long LastTotalMilliseconds;

            /// <summary>
            /// Stopwatch which we can use to avoid swamping the log with too many messages for threading violations.
            /// </summary>
            [NonSerialized]
            private Stopwatch sw;

            /// <summary>
            /// Invoked only once at runtime from the static constructor of type declaring the target method. 
            /// </summary>
            /// <param name="method"></param>
            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;
            }

            /// <summary>
            /// Invoked at runtime before that target method is invoked.
            /// </summary>
            /// <param name="args">Arguments to the function.</param>   
            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;
            }
        }
    }

我可以按需提供完整的演示项目.

I can provide the full demo project on demand.

这篇关于如何获得一个ID,该ID可以区分一个类的不同实例?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆