3

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
  • Updated my answer to include an example. I'm sure you can figure it out from there. Commented Jul 29, 2011 at 16:09

3 Answers 3

4

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

    }
}
Sign up to request clarification or add additional context in comments.

6 Comments

Good answer. The tricky thing with constructors is that it's hard to define when it is "after" the constructor. Several constructors can be chained, so how do you determine when construction is "done"? It's not trivial. Ideally, one should add a depth-tracking parameter in all constructors.
@Gael Fraiteur, I thought of this too but I figured that's just something that would have to be dealt with on a per-case basis. I'm not exactly sure what his usage is so I didn't bother trying to solve for it.
As I'm writing a location-level aspect, I went the other way: I derived from LocationLevelAspect and added an OnMethodEntryAdvice with a MethodPointcut. Thanks a lot for the links!
@DustinDavis, Gael: it seems that I'm having the chained-constructor problem you mentioned above. If the target type has multiple constructors calling each other, I currently have no way of knowing when the last one has finished. Does the user really have to introduce a depth parameter to all constructors (which would make the aspect rather ugly to use)? Is there any other way?
Theoretically, you could use PostSharp to automatically create constructor overloads with depth parameter (I did such constructor overload scheme for a customer). But you would have to use PostSharp SDK, and it's where we usually start to say "it's impossible". It all depends on the scale of the problem you want to solve and the time you want to invest in the solution.
|
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!
            }
        }
    }

2 Comments

This doesn't exclude multiple constructors within the top (last) class yet.
Yes Steven, you are right. That problem could be easily handled by adding OnEnter logic for constructors only so as to track the first constructor called from this class which will be the top most constructor for this class. In that case the OnExit logic should be handled only for that first constructor called.
1

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.