Re[3]: Множественное наследование
От: Dax  
Дата: 21.07.04 16:05
Оценка: 17 (3)
Здравствуйте, 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 >>
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.