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
Post a Comment