Здравствуйте, Аноним, Вы писали:
А>На одном собеседовании был вопрос: возможно ли на C# реализовать как-нибудь множественное наследование (дабы не переписывать написанный код).
А>Я ответил что-то в духе того что в общем случае вроде бы нет. Они как-то странно посмотрели И кстати потом не взяли...
А>Какой правильный ответ?
Не возможно. А что за частные случаи когда ты думаешь что возможно?
Hello, > На одном собеседовании был вопрос: возможно ли на C# реализовать как-нибудь множественное наследование (дабы не переписывать написанный код). > > Я ответил что-то в духе того что в общем случае вроде бы нет. Они как-то странно посмотрели И кстати потом не взяли... > Какой правильный ответ?
Есть множественное наследование интерфейсов. Дальше можно реализовать автоматическое аррегирование реализаций уже написанных классов и используя множественное наследование интерфейсов можно имитировать обычное множественное наследование.
Posted via RSDN NNTP Server 1.9 alpha
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
Hello, TK!
T> Hello,
??>> На одном собеседовании был вопрос: возможно ли на C# реализовать
??>> как-нибудь множественное наследование (дабы не переписывать написанный
??>> код). Я ответил что-то в духе того что в общем случае вроде бы нет.
??>> Они как-то странно посмотрели И кстати потом не взяли... Какой
??>> правильный ответ?
T> Есть множественное наследование интерфейсов. Дальше можно реализовать T> автоматическое аррегирование реализаций уже написанных классов и T> используя множественное наследование интерфейсов можно имитировать T> обычное множественное наследование.
Вопрос: как можно реализовать "автоматическое аррегирование реализаций уже написанных классов" ?
p.s.
Вообще, по моему, если так рассуждать, то и на си (просто си) можно множественное наследованяие реализовать "как-нибудь", даже не смотря на то, что в языке классов нет.
Вроде бы отличие наследования от агрегирования как раз и заключается в том, что не нужно делегировать реализацию кому-либо. Грубо говоря, эта самая ссылка на родителя автоматически создается компилятором (на родительскую vmt), а частично код родителя (грубо говоря) копируется в потомка (тоже компилятором).
Hello, "Vitaton" > > > TK>Есть множественное наследование интерфейсов. Дальше можно реализовать автоматическое аррегирование реализаций уже написанных классов > > В двух словах, как?
В двух словах — есть класс реализующий интерфейс IImplementation, есть класс и CompoundObject для класса CompoundObject определяем фабрику класса котрая делает следущее — генерирует новый класс, наследник CompoundObject добавляя в него поле содержащее ссылку на класс реализующий IImplementation и сам интерфейс IImplementation реализация которого заключается в делегировании всех вызовов через добавленное поле. После этого фабрика возвращает ссылку на созданный экземпляр уже нового класса.
Если поискать на форуме, то можно найти пример реализации.
Posted via RSDN NNTP Server 1.9 alpha
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
Hello, "s.ts" > > Вопрос: как можно реализовать "автоматическое аррегирование реализаций уже написанных классов" ? >
System.Reflection.Emit и жедание.
> p.s. > Вообще, по моему, если так рассуждать, то и на си (просто си) можно множественное наследованяие реализовать "как-нибудь", даже не смотря на то, что в языке классов нет.
Естественно, множественного наследования классов в .NET как не крути — нет. Но, можно создавать суррогаты которые при желании помогут достаточно прозрачно получить нужную функциональность.
Posted via RSDN NNTP Server 1.9 alpha
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
Здравствуйте, TK, Вы писали:
TK>Естественно, множественного наследования классов в .NET как не крути — нет. Но, можно создавать суррогаты которые при желании помогут достаточно прозрачно получить нужную функциональность.
А статейку забульбенить?
... << Rsdn@Home 1.1.4 beta 1 >>
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, IT, Вы писали:
IT>>А статейку забульбенить?
AVK>А оно кому нибудь надо? ИМХО в реальности никто этим пользоваться не будет.
Ага. В суровой реальности приходится таким пользоваться.
Типа лучше по клаве стучать, чем шаманством заниматься.
interface IBase2:IBase1
{
//....
}
class C : IBase2
{
IBase1 _b;
C(IBase1 b){_b=b;}
void IBase2.Func1(){_b.Func1();}
void IBase2.Func2(){_b.Func2();}
//...
//и.т.д до посинения
}
Здравствуйте, 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#, (будем играть по его правилам) декларируем интерфейсы (для того, чтобы было "в двух словах", абсолютно синтетические ):
интерфейс класса, в который будем подмешиваться
Теперь сами классы...
Главный класс, в который вся наша мешанина будет заливаться
// т.к. экземпляр наследника этого класса однозначно будет сгенерирован в рантайме, воспользумся генератором,
// чтобы реализовать абстрактные проперти (генератор можно научить понимать какие-нибудь хинты-атрибуты
// типа, что делать в случае null value или какие exceptions ловить молча, а какие логировать или выбрасывать наверхpublic abstract class User: IUser, With<FullNameUserMixin>, With<CryptoNameUserMixin> {
public abstract string FirstName {get;set;} // генератор объявит string fieldFirstName и реализует соотв. get/setpublic abstract string LastName {get;set;} // генератор объявит string fieldLastName и реализует соотв. get/setpublic 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);
}
}
}
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, IT, Вы писали:
IT>>А статейку забульбенить?
AVK>А оно кому нибудь надо? ИМХО в реальности никто этим пользоваться не будет.
Надо. А то у самого всё руки не доходят это сделать
S_>Ага. В суровой реальности приходится таким пользоваться.
Вероятнее всего, это не суровая реальность, а суровая ошибка в дизайне
Особенно, если как ты говоришь, "приходится до посинения". Я думаю, финансово наказывать таких архитекторов неправильно, а лучше делать кадровые перестановки.