Generics und Codeverdoppelung
-
Ist mehr so eine Grundsatzfrage, die durch die starke Typisierung ausgelöst wird.
Das Programm arbeitet mit dem PE Header von ausführbaren Dateien. Für 32/64Bit Dateien sieht der Header minimal anders aus (manche Einträge sind 64Bit statt 32Bit lang). Bisher war der Code nur für 32Bit ausgelegt.
class PE32 { public UInt32 AddressOfEntryPoint; } void foo(PE32 pe) { //mach viele Sachen mit pe bar(pe.AddressOfEntryPoint); //mach viele Sachen mit pe }
Bei 64Bit sieht der entsprechende Eintrag im Header so aus:
class PE64 { public UInt64 AddressOfEntryPoint; }
Ich wollte jetzt aus foo eine generische Methode machen, die entweder PE32 oder PE64 als Parameter nehmen kann. bar wäre eine Methode mit zwei Überladungen geworden, da deren Code spezifisch für 32/64Bit ist. mach viele Sachen mit pe ist allerdings bei beiden Headern gleich. Eine normale generische Methode scheidet aus, da natürlich dann kein Zugriff auf AddressOfEntryPoint bestehen würde. Beide PE Klassen von einer gemeinsamen Basis erben zu lassen, habe ich bisher nicht zustande gebracht, da es durch den unterschiedlichen Datentyp keine Gemeinsamkeiten als Interface gibt.
Einfachste Lösung des Problems wäre für beide PE Varianten Überladungen von foo anzubieten. Das führt aber zu Codeduplizierung. Ein untypisiertes Template von C++ wäre das sauberste. Wie ist dabei das "beste" Vorgehen in C#?
-
Mach ein Interface das sowohl
PE32
als auchPE64
implementieren.
Das Interface muss dabei natürlich nenUInt64
für dieAddressOfEntryPoint
Property verwenden. Die class (oder struct)PE32
kann aber als Backing-Member für die Interface-Property nenUInt32
verwenden.
-
abstract class PE { public abstract UInt64 GetAddressOfEntryPoint(); } class PE32 : PE { public UInt32 AddressOfEntryPoint; public override UInt64 GetAddressOfEntryPoint() { return AddressOfEntryPoint; } } class PE64 : PE { public UInt64 AddressOfEntryPoint; public override UInt64 GetAddressOfEntryPoint() { return AddressOfEntryPoint; } } static void foo<T>(T pe) where T : PE { //mach viele Sachen mit pe bar(pe.GetAddressOfEntryPoint()); //mach viele Sachen mit pe } void bar(UInt32 x) { } void bar(UInt64 x) { }
So könnte das funktionieren, allerdings muss dann bar umgebaut werden, da nun nicht mehr die Unterscheidung int UInt32/64 funktioniert, da alles einheitlich 64Bit ist.
Für andere Vorschläge bin ich weiterhin offen.
-
Hi
static void foo<T>(T pe) where T : PE
Wozu Generic? Der Parameter kann direkt vom Typ PE sein.
Vorschlag:
using System; namespace VonKanonenUndSpatzen { class Program { static void Main(string[] args) { PE32 pe1 = new PE32 { AddressOfEntryPoint = 32, CommonData = "32 Bit" }; foo(new Proc32(pe1), pe1); Console.WriteLine(); PE64 pe2 = new PE64 { AddressOfEntryPoint = 64, CommonData = "64 Bit" }; foo(new Proc64(pe2), pe2); Console.ReadKey(true); } static void foo(Proc proc, PE pe) { Console.WriteLine("begin {0}", pe.CommonData); proc.bar(); Console.WriteLine("end {0}", pe.CommonData); } } class PE { public string CommonData { get; set; } } class PE32 : PE { public UInt32 AddressOfEntryPoint { get; set; } } class PE64 : PE { public UInt64 AddressOfEntryPoint { get; set; } } interface Proc { void bar(); } class Proc32 : Proc { PE32 pe32; public Proc32(PE32 pe32) { this.pe32 = pe32; } public void bar() { Console.WriteLine(pe32.AddressOfEntryPoint.GetType()); } } class Proc64 : Proc { PE64 pe64; public Proc64(PE64 pe64) { this.pe64 = pe64; } public void bar() { Console.WriteLine(pe64.AddressOfEntryPoint.GetType()); } } }
-
ja, dann ist es natürlich Quatsch an der Stelle Generics zu benutzen. Aus bar einen Member der Datenklasse zu machen, wäre auch eine Idee. Danke für den Input, ich teste da mal weiter.
-
KN4CK3R schrieb:
Aus bar einen Member der Datenklasse zu machen, wäre auch eine Idee. Danke für den Input, ich teste da mal weiter.
bar ist nicht Member der Datenklasse, schau nochmal genau hin
-
ja, sry -.-
Habs jetzt so ähnlich gemacht, nur dass Proc die gemeinsamen Daten weiterleitet, so dass foo nur Proc als Parameter übergeben bekommt, anstatt beide.