Здравствуйте, Vitaton, Вы писали:
V>В двух словах, как?
1. Вводим декларативный интерфейс With для обозначения интерфейсов, которые будут автоматически подмешены и исходному классу.
// декларировать модмешиваемые интерфейсы можно и аттрибутами,
// но ввиду известных ограничений generics мы пришли к варианту интерфейсов
interface With<T>{}
2. Для пущей наглядности заводим базовый класс для всех будущих миксинов (для тех, кто встретил новое слово — гуглим по "A Core Calculus of Classes and Mixins. Viviana Bono1, Amit Patel, and Vitaly Shmatikov"):
class MixinBase<T> {
protected T Next;
public MixinBase(T next){ this.Next = next; }
}
3. Ввиду статической природы типизации C#, (будем играть по его правилам) декларируем интерфейсы (для того, чтобы было "в двух словах", абсолютно синтетические

):
интерфейс класса, в который будем подмешиваться
interface IUser {
string FirstName{get;set;}
string LastName{get;set;}
int? Age;
}
и интерфейсы, которые будут подмешиваться
interface IFullNamedUser {
string FullName { get;set; }
}
interface ICryptoNamedUser {
string CryptoName { get; }
}
Теперь сами классы...
Главный класс, в который вся наша мешанина будет заливаться
// т.к. экземпляр наследника этого класса однозначно будет сгенерирован в рантайме, воспользумся генератором,
// чтобы реализовать абстрактные проперти (генератор можно научить понимать какие-нибудь хинты-атрибуты
// типа, что делать в случае null value или какие exceptions ловить молча, а какие логировать или выбрасывать наверх
public abstract class User: IUser, With<FullNameUserMixin>, With<CryptoNameUserMixin> {
public abstract string FirstName {get;set;} // генератор объявит string fieldFirstName и реализует соотв. get/set
public abstract string LastName {get;set;} // генератор объявит string fieldLastName и реализует соотв. get/set
public abstract int? Age {get;set;} // генератор объявит Nullable<int> fieldAge и реализует соотв. get/set
}
И синтетический миксины для него:
// на самом деле миксины могут наследоваться от других миксинов или классов
// етим и достигается "множественное" наследование (с определенными ограничениями, конечно, но это уже не "в двух словах")
class FullNameUserMixin: MixinBase<IUser>, IFullNamedUser {
public FullNameUserMixin(IUser next): base(next){}
public string FullName {
get { return string.Concat(this.Next.FirstName ?? "DefaultFName", " ", this.Next.LastName ?? "DefaultLName", ", ", this.Age ?? 33); }
set { string[] n =(value ?? "DefaultFName DefaultLName").Split(' '); this.Next.FirstName = n[0]; this.Next.LastName = n[1];}
}
}
class CryptoNameUserMixin: MixinBase<IUser>, ICryptoNamedUser {
MyEncoder encoder;
string hash;
public CryptoNameUserMixin(IUser next): base(next) {
this.encoder = new MyEncoder();
this.hash = MyRandomHashProvider.GenerateHash();
}
public string CryptoName {get { return this.encoder.Encode(this.Next.LastName + this.Next.FirstName + this.hash}}
}
Теперь использование:
// MyStaticFab сгенерирует класс, унаследованный от User, реализует его
// абстрактные методы и интерфейсы миксинов, указанных в With<T>,
// добавит для каждого миксина private field и в конструкторе класса
// проинициализирует их соотв. значениями ( типа, this.someMix = new FullNameUserMixin(this); )
// обработку методов микс-интерфейсов делегирует соотв. методам this.someMix см ниже реальный код (ImplementTrait)
User user = MyStaticFab.Generate<User>();
user.FirstName = "John"; user.LastName = "Smith";
IFullNamedUser foo = user as IFullNamedUser;
ICryptoNamedUser bar = user as ICryptoNamedUser;
Console.WriteLine("FullName is {0}", foo.FullName);
Console.WriteLine("CryptoName is {0}", bar.CryptoName);
Это метод реально работающего генератора, может, для ясности кому-нить понадобится
private void ImplementTrait(TypeBuilder tb, ILGenerator cb, Type trait) {
ILGenerator il = null;
FieldBuilder fb = tb.DefineField(trait.Name.ToLower(), trait, FieldAttributes.Private | FieldAttributes.InitOnly);
ConstructorInfo ci = trait.GetConstructors()[0];
cb.Emit(OpCodes.Ldarg_0); // add code for initializer - this.currentTrait = new CurrentTrait(this);
cb.Emit(OpCodes.Ldarg_0);
cb.Emit(OpCodes.Newobj, ci);
cb.Emit(OpCodes.Stfld, fb);
foreach(Type iface in trait.GetInterfaces()) { // imp all trait interfaces
tb.AddInterfaceImplementation(iface);
foreach(MethodInfo mi in iface.GetMethods()) {
ParameterInfo[] pis = mi.GetParameters();
Type[] ts = new Type[pis.Length];
for(int i=0; i<pis.Length; i++) {
ts[i] = pis[i].ParameterType;
}
MethodBuilder mb = tb.DefineMethod(iface.FullName + "+.+" + mi.Name, MethodAttributes.Private | MethodAttributes.Virtual | MethodAttributes.Final, mi.ReturnType, ts);
il = mb.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, fb);
for(int i=1; i<= pis.Length; i++) {
this.EmitLdarg(il, i);
}
il.Emit(OpCodes.Callvirt, mi);
il.Emit(OpCodes.Ret);
tb.DefineMethodOverride(mb,mi);
}
}
}
... << silent >>