Self-types in NRoles

Since roles are weaved into target classes, it's sometimes necessary for them to be able to identify the type of these classes.

NRoles supports self-types through the use of a type parameter with the special name S:

public abstract class REquatable<S> : IEquatable<S>, Role {
  public abstract bool Equals(S other);
  public bool Differs(S other) {
    return !Equals(other);
  }
}

To cast this to the type parameter, just use the Cast<T> extension method:

public class Identity<S> : Role {
  public S Self {
    get { return this.Cast<S>(); }
  }
}

When other roles compose roles with self-types, they must flow the self-type through, since it will ultimately be determined by the target classes that compose them:

public abstract class RComparable<S> : 
  IComparable<S>, Does<REquatable<S>>, Role {
  ...
}

The post-compiler will check all classes that compose roles with self-types and issue an error message if the type parameter is not the target class itself.

public sealed class Money : 
  Does<RComparable<Money>>, Does<REquatable<Money>> {
  ...
}

Constraints added to the self-type become requirements for the target classes to fulfill:

public class MyRole<S> : Role
  where S : ISomeInterface {
  ...
}

public class MyClass : ISomeInterface,
  Does<MyRole<MyClass>> {
  ...
}

This way, roles can work with the type of the classes that compose them generically and safely. It's useful, for example, for fluent interfaces that return the type of this, used for fluent method chaining:

public class PropertyBag<S> : Role {
  public S With(string name, object value) {
    ...
    return this.Cast<S>();
  }
}

Comments

Popular posts from this blog

The Acyclic Visitor Pattern

Some OO Design

NRoles: An experiment with roles in C#