Question

I am unexperienced with Aspect-Oriented Programming. However, I've read a fair amount of PDFs and documentation available from PostSharp, and I think that I understand the gist of the paradigm. I have a pretty unique problem, and I believe AOP can be used to solve it. My predicament is as follows:

Many classes will inherit from A, which can be enabled or disabled. Consider B, which extends A. If B is disabled, I would like all method execution and property and variable access/modification to be disabled. That is, B.ExecuteMethod(); and B.Property = newValue; will have no effect if B is disabled. Furthermore, if one expects a return value, the value will be defaulted to 0 or null if B is disabled. That is, I would like to have expected default values for objects and values.

I am using the PostSharp C# library, which seems very powerful and well-developed. I believe my problem can be solved by means of AttributeInheritance. For example, A can be defined as:

[ModularAttribute(AttributeInheritance = MulticastInheritance.Multicast)]
public class A {

    private bool m_enabled;

    public A(){
        m_enabled = true;
    }

    public bool Enabled() {
        get {
            return m_enabled;
        }
        set {
            m_enabled = value;
        }
    }
}

and B can extend A. Moreover, my attribute, ModularAttribute can be defined as:

[Serializable]
public sealed class ModularAttribute : OnMethodBoundaryAspect {

    public ModularAttribute() {
    }

    public override void OnEntry(MethodExecutionArgs args) {
        // only execute code if enabled
    }
}

This attribute will be applied to B because B extends A.

The root of my problem is: I need ModularAttribute to reference A's Enabled property, such that OnEntry will only execute code if Enabled is true. Since this is a class-level aspect, I cannot parameterize a wrapped version of m_enabled to ModularAttribute since it is out of scope.

Is there a way that I can tell ModularAttribute that all of its owners will implement a specific interface? If so, could ModularAttribute access the specific properties from said interface? If so, this would solve my problem.

To clarify, I would like to "tell" PostSharp: "The class that uses ModularAttribute is guaranteed to implement C. So, let ModularAttribute access whatever C defines because it's ensured to work."

C can be defined as:

public interface C { 
    public bool Enabled();
}

Thus, in ModularAttribute, I could do something along the lines of

if (attachedClass.Enabled == false) { 
    // don't execute code
} else {
    // execute code
}

This problem can be perceived as authentication on the per-object level rather than the more typical per-user level. Having to add an if, else check on every Property and Method that extends A seems like a cross-cutting concern. Thus, I think AOP is a fitting choice for this problem; however, because of my inexperience with this paradigm, I might be approaching it the wrong way.

Any guidance would be much appreciated. Thanks for the help,

Was it helpful?

Solution

I'm a little concerned that this much inheritance could be a design flaw or at least a huge maintenance headache, but assuming that it's not, let's soldier on...

I don't think there's a way to do exactly what you want to do. Even if PostSharp had the ability, C# needs to know the type at compile time (before PostSharp even touches it).

I suggest that you use CompileTimeValidate to verify that the class the aspect is used on is of a certain type, and once that's in place, you can cast args.Instance to your interface type without worrying about an invalid cast exception. And if that class doesn't implement IEnabled, then you'll get a compile-time error.

Here's a quick example:

public interface IEnabled
{
    bool Enabled { get; }
}

[Serializable]
public class ModularAttribute : OnMethodBoundaryAspect
{
    public override bool CompileTimeValidate(System.Reflection.MethodBase method)
    {
        if(typeof(IEnabled).IsAssignableFrom(method.DeclaringType))
            return true;
        Message.Write(method, SeverityType.Error, "MYERR001", "Aspect can't be used on a class that doesn't implement IEnabled");
        return false;
    }

    public override void OnEntry(MethodExecutionArgs args)
    {
        var obj = (IEnabled) args.Instance; // this will always be a safe cast
        if(!obj.Enabled)
            args.FlowBehavior = FlowBehavior.Return;
    }
}

There's a catch though: you don't want this aspect being used on the Enabled property itself, because that would cause a stack overflow (i.e. the aspect checks the property, causing the aspect to check the property, etc). So make sure to exclude Enabled using AttributeExclude.

class Program
{
    static void Main(string[] args)
    {
        var b = new B();
        b.Enabled = false;
        b.SomeMethod();
        b.AnotherMethod();
    }
}

public interface IEnabled
{
    bool Enabled { get; }
}

[Modular(AttributeInheritance = MulticastInheritance.Multicast)]
public class A : IEnabled
{
    [Modular(AttributeExclude = true)]
    public bool Enabled { get; set; }

    public void SomeMethod()
    {
        Console.WriteLine("in SomeMethod");
    }
}

public class B : A
{
    public void AnotherMethod()
    {
        Console.WriteLine("in AnotherMethod");
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top