typename T::InnerType in C#



  • Hi,

    wie setze ich folgendes in C# um:

    template<class T>
    class Test
    {
        typename T::InnerType func();
    }
    

    Folgendes:

    abstract class ConvertibleType<T>
    {
        ?T? InnerType ?    
    }
    
    class DecString : ConvertibleType<string>
    {
    
    }
    
    interface IConvertibleTo<T> where T : ?
    {
       ? T::InnerType ConverTo();
    }
    

    Damit ich nachher so machen kann:

    class IPAddr : IConvertibleTo<DecString>, IConvertible<HexString>
    {
        ?T::InnerType IConvertibleTo<DecString>.ConverTo()
        {
            // make byte[] to 192.43.23.22
            return "192.43.23.22"
        }
    
        ?T::InnerType IConvertibleTo<HexString>.ConverTo()
        {
            // make byte[] to 0x45 0x35 0x34 0xcc
            return "0x45 0x35 0x34 0xcc"
        }
    }
    

    Im Prinzip sind ConvertibleType's Repräsentationen und InnerType der Datentyp in dem diese Repräsentation zurückgegeben werden kann.



  • KasF schrieb:

    wie setze ich folgendes in C# um:

    Nicht über Generics (zumindest nicht idealerweise). Generics kennen kein typename und somit müsstest du den InnerType-typparameter immer als zusätzlichen Typparameter rumschleppen.
    Ich würde es eher klassisch angehen, d.h. Basisklasse, davon dann die DecString und HexString ableiten und 'ne virtuelle Methode überschreiben. Im Beispiel ist der Rückgabetyp ja eh immer string (vmtl. Vereinfachung für's Beispiel, oder?), d.h. es unterscheidet sich nur der Algorithmus, wie man vom byte-Array zum string kommt.



  • Leider nicht, es gibt auch Typen die als ints etc zurückgeliefert werden sollen.
    Zb habe ich ne Bezeichnung für ein bestimmten Format -> CoolFormat mit InnerType gleich UInt16. Es soll halt so generisch wie Möglich sein.

    Ich will einerseits die Klassen haben die mir meine Darstellung kapseln und dazu einen inneren Typ der mir diese Darstellung zurück liefert.



  • Die Scheißlösung ist folgende:

    void Main()
    {	
    	IPAddr ip = new IPAddr();
    	Console.WriteLine(ip.DoConvert<DecStringConverter, string>(new DecStringConverter()));
    	Console.WriteLine(ip.DoConvert<HexStringConverter, string>(new HexStringConverter()));
    	Console.WriteLine(ip.DoConvert<IntConverter, int>(new IntConverter()));
    }
    
    abstract class Converter<TargetType> {
    	public Type InnerType {
    		get { return typeof(TargetType); }
    	}
    
    	public abstract TargetType Convert(byte[] source);
    }
    
    class DecStringConverter : Converter<string> {
    	public override string Convert(byte[] source) {
    		return "192.43.23.22";
    	}
    }
    
    class HexStringConverter : Converter<string> {
    	public override string Convert(byte[] source) {
    		return "0x45 0x35 0x34 0xcc";
    	}
    }
    
    class IntConverter : Converter<int> {
    	public override int Convert(byte[] source) {
    		return 42;
    	}
    }
    
    class IPAddr {
    	public TargetType DoConvert<ConvertType, TargetType>(ConvertType ct) where ConvertType : Converter<TargetType> {
    		byte[] array = null;
    		return ct.Convert(array);
    	}
    }
    

    Aber das ist alles andere als optimal. Ich überlege aber gerade noch, ob man was mit Reflection machen kann. Das wird dann aber teuer.

    Edit: Ich sehe gerade, dass man das InnerType eh streichen kann. Und auch mit Reflection wird man nicht wirklich weiterkommen.



  • Das Problem ist ja am ende nur das man 2 Methoden hat die sich nicht an einem Parameter unterscheiden, dh es ist nicht implementierbar.

    public interface IConverter<T>
    {
        T Convert();
    }
    
    public class IPAddress : IConverter<string>, IConverter<int>
    {
        public string Convert()
        {
            return "Demo";
        }
    
        public int Convert() // Meeeeep
        {
            return 5;
        }
    }
    

    Ich würde aber auch nicht die Klasse "IPAdress" so verschmutzen, ich würde eine Converter klasse schreiben.

    public class IPAddress
    {
        public string GetText() { return IPAddressConverter.ToText(this); }
        public int GetInt() { return IPAddressConverter.ToInt(this); }
    }
    
    public static class IPAddressConverter
    {
        public static string ToText(IPAddress address) // ToString doof ^^
        {
            return "Demo"; // Arbeiten mit den "echten" Daten in IPAddress
        }
    
        public static int ToInt(IPAddress address)
        {
            return 5;
        }
    }
    

    Nur so als Idee.



  • David W schrieb:

    Das Problem ist ja am ende nur das man 2 Methoden hat die sich nicht an einem Parameter unterscheiden, dh es ist nicht implementierbar.

    public interface IConverter<T>
    {
        T Convert();
    }
    
    public class IPAddress : IConverter<string>, IConverter<int>
    {
        public string Convert()
        {
            return "Demo";
        }
    
        public int Convert() // Meeeeep
        {
            return 5;
        }
    }
    

    Nicht wirklich, denn ich mache dann sowas:

    public class IPAddress : IConverter<string>, IConverter<int>
    {
        public IConverter<string>.string Convert()
        {
            return "Demo";
        }
    
        public int IConverter<int>.Convert() // Meeeeep
        {
            return 5;
        }
    
        public T getValueAs<T>()
        {
             return ((IConverter<T>)this).Convert();
        }
    }
    
    IPAddress a = ...
    a.getValueAs<string>();
    a.getValueAs<int>();
    

    🙂

    Über die zwei anderen Vorschläge grübele ich gerade, was passender hier für mich wäre.



  • KasF schrieb:

    Nicht wirklich, denn ich mache dann sowas:

    IPAddress a = ...
    a.getValueAs<string>();
    a.getValueAs<int>();
    

    Wenn du eh sowas machst, dann go for it:

    void Main()
    {
    	IPAddr ip = new IPAddr();
    	Console.WriteLine(ip.GetValueAs<string>(new DecStringConverter()));
    	Console.WriteLine(ip.GetValueAs<int>(new IntConverter()));
    }
    
    abstract class Converter {
    	public abstract Object Convert(byte[] source);
    }
    
    class DecStringConverter : Converter {
    	public override Object Convert(byte[] source) {
    		return "192.43.23.22";
    	}
    }
    
    class IntConverter : Converter {
    	public override Object Convert(byte[] source) {
    		return 42;
    	}
    }
    
    class IPAddr {
    	public TargetType GetValueAs<TargetType>(Converter c) {
    		byte[] array = null;
    
    		Object result = c.Convert(array);
    		if (result.GetType() == typeof(TargetType))
    		    return (TargetType)result;
    		else
    			return default(TargetType);
    	}
    }
    

    Du kannst die Converter selbst natürlich auch innerhalb der Klasse oder in einer anderen Struktur verwalten, damit du nicht jedes mal "new DecStringConverter()" schreiben musst. Ich denke das hier ist der sauberste Ansatz und besser als mein erster Gedanke.



  • GPCs Lösung sieht echt gut aus 🙂
    Nur der typen check gefällt mir nicht so recht - eventuell ist es besser es knallen zu lassen statt einen falsch Aufruf nicht mit zu bekommen und mit dem Default Wert zu arbeiten.

    public class IPAddr
    {
        public T GetValueAs<T>(Converter c)
        {
            return (TargetType)c.Convert(myData);
        }
    }
    
    [TestMethod]
    [ExpectedException(typeof(InvalidCastException))]
    public void GetValueAs_StringTypeWithIntegerConverter_ThrowsInvalidCastException()
    {
        var ip = new IPAddr();
        ip.GetValueAs<string>(new IntConverter()));
    }
    

    Wobei ich sagen muss das diese Lösung schon fast meine ist, und zwar das man einen Externen Converter die Arbeit machen lässt ^^

    var value = ip.GetValueAs<string>(new IntConverter()));
    //vs
    value = IntConverter.Convert(ip); // static statt instanz
    


  • GPC schrieb:

    ...

    Like 👍 Geht in Richtung Strategie-Pattern. Gerade im Meeting wurden nochmals die ganzen Anforderungen gesammelt, das Ganze ist noch ein bissl verzwickter und komplizierter und ich muss darüber nochmal gründlich nachdenken, aber vielen Dank für die Ideen, damit kann ich bestimmt was anfangen.


Anmelden zum Antworten