PostSharp:在目標構造函數之後初始化實例範圍的方面 (PostSharp: initialize instance-scoped aspect after target constructors)


問題描述

PostSharp:在目標構造函數之後初始化實例範圍的方面 (PostSharp: initialize instance-scoped aspect after target constructors)

I have written an aspect implementing IInstanceScopedAspect and inheriting from LocationInterceptionAspect.

When initialized, it reads some properties from the target object. The problem is that IInstanceScopedAspect.RuntimeInitializeInstance() is called before the target object's constructors have run. So the target object is not fully initialized and reading its properties leads to all kinds of nasty behavior.

How can I be notified when the target object has been fully initialized (that is, all its constructors have run)? My attribute is not applied directly to the target class but to one or more of its properties.


參考解法

方法 1:

The RuntimeInitializeInstance is meant to initialize your aspect when PostSharp creates a new instance. It's going to always be called before your class is initialized. What exactly is your aspect needing to do at the point of instantiation of the object when your aspect is a locationinterceptionaspect? 

I suggest creating a complex aspect that includes an OnExit advice that targets constructors and this advice will be run after any constructor runs. In the OnExit advice, do the work you need there. your object will be initialized by then.

So your complex aspect would include your locationinterception advices and an additional OnMethodExitAdvice.

Check out these articles on how to do this: 

http://www.sharpcrafters.com/blog/post/PostSharp-Principals-Day-12-e28093-Aspect-Providers-e28093-Part-1.aspx

http://www.sharpcrafters.com/blog/post/PostSharp-Principals-Day-13-e28093-Aspect-Providers-e28093-Part-2.aspx

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            ExampleClass ec = new ExampleClass();

            ec.ID = 10;

            Console.ReadKey();
        }
    }

    [ComplexAspect]
    class ExampleClass
    {
        public int ID { get; set; }
        public string Name { get; set; }

        public ExampleClass()
        {
            //Setup
            Name = "John Smith";
        }
    }

    [Serializable]
    public class ComplexAspect : InstanceLevelAspect
    {
        [OnMethodExitAdvice, MulticastPointcut(MemberName=".ctor")]
        public void OnExit(MethodExecutionArgs args)
        {
            //Object should be initialized, do work.
            string value = ((ExampleClass)args.Instance).Name;
            Console.WriteLine("Name is " + value);
        }

        [OnLocationGetValueAdvice, MulticastPointcut(Targets=MulticastTargets.Property )]
        public void OnGetValue(LocationInterceptionArgs args)
        {
            Console.WriteLine("Get value for " + args.LocationName);
            args.ProceedGetValue();
        }

        [OnLocationSetValueAdvice, MulticastPointcut(Targets = MulticastTargets.Property)]
        public void OnSetValue(LocationInterceptionArgs args)
        {
            Console.WriteLine("Set value for " + args.LocationName);
            args.ProceedSetValue();
        }

        public override void RuntimeInitializeInstance()
        {
            base.RuntimeInitializeInstance();
        }

    }
}

方法 2:

Although this is an old question, I have figured out an easy way to identify the OnExit for the top (last) constructor in a hierarchy. 

What I do is store the class type at compile type and then check if the class type of the active constructor is the same as the Instance type of this object.

In that case we know we are in the top (last) constructor and we can safely use every class member, even virtual ones.

Please see the following code:

    [Serializable]
    [MulticastAttributeUsage(MulticastTargets.Class, AllowMultiple=false, Inheritance = MulticastInheritance.Multicast)]
    public class PostConstructorAttribute : InstanceLevelAspect {

        private Type _classType;

        public override void CompileTimeInitialize(Type type, AspectInfo aspectInfo) {
            //Assign the class type to the _classType variable,
            //At compile time this will always be the type of the class we are currently in.
            _classType = type;

            base.CompileTimeInitialize(type, aspectInfo);
        }

        [OnMethodExitAdvice, MulticastPointcut(MemberName = ".ctor")]
        public void OnExit(MethodExecutionArgs args)
        {
            //Instance is the top most type of the hierarchy, 
            //so if _classType is the top most type then we are in the top most constructor!
            if (Instance.GetType() == _classType) {
                //We are at the top most constructor and after all constructors have been called.
                //Everything is setted up now and we can safely use virtual functions

                //Magic happens here!
            }
        }
    }

方法 3:

For the googlers (like me) who run into this issue

the OnInstanceConstructed Advice solves this problem out of the box http://doc.postsharp.net/t_postsharp_aspects_advices_oninstanceconstructedadvice

(by Daniel WolfDustin DavisGeorge MavritsakisBatavia)

參考文件

  1. PostSharp: initialize instance-scoped aspect after target constructors (CC BY-SA 3.0/4.0)

#postsharp #C#






相關問題

拋出自定義異常並使用 Postsharp 捕獲它們 (Throw an custom exception and catch them with Postsharp)

Làm cách nào để loại trừ khỏi ghi nhật ký dựa trên OnMethodBoundaryAspect? (How to exclude from OnMethodBoundaryAspect-based logging?)

在屬性中傳遞動態參數 (PostSharp) (Passing dynamic parameters in attributes (PostSharp))

PostSharp 3.0 中 IAspectProvider 的重大變化? (Breaking change of IAspectProvider in PostSharp 3.0?)

將方面應用於其他程序集類方法調用 (apply an aspect to other assembly class methods calls)

Postsharp 和 log4net 和 log4postsharp (Postsharp and log4net and log4postsharp)

Postsharp - 獲取調用程序集? (Postsharp - Get Calling Assembly?)

PostSharp 對象映射器 (PostSharp for an object mapper)

PostSharp - 自動化事件訂閱和集合添加 (PostSharp - automate event subscription and collection addition)

PostSharp:在目標構造函數之後初始化實例範圍的方面 (PostSharp: initialize instance-scoped aspect after target constructors)

使用 PostSharp 1.0 的 ClickOnce 應用程序似乎需要 GAC 中的 1.5 程序集 (ClickOnce application that uses PostSharp 1.0 seems to require 1.5 assemblies in GAC)

更改用戶控件 DependencyProperty 時屬性重置 (Property resets when changing Usercontrols DependencyProperty)







留言討論