Laden...

generische Typkonvertierung

Erstellt von antoschka vor 15 Jahren Letzter Beitrag vor 15 Jahren 8.040 Views
A
antoschka Themenstarter:in
371 Beiträge seit 2007
vor 15 Jahren
generische Typkonvertierung

Hallo,

ich habe eine Frage ich möchte ein Property vom Typ Objekt setzen und habe einen String-Wert (Daten) und einen Typ (System.Type)
Hat jemand eine Idee, wie man am Besten den StringWert in den Typ konvertiert um ihn dann dem Property zuzuweisen.
Falls die generische Konvertierung (Parsing??) nicht klappt soll null zugewiesen werden.

Besten Dank für Eure Hilfe antoschka

Gelöschter Account
vor 15 Jahren

kannst du in einem kurzen codesample zeigen was genau du meinst?

A
antoschka Themenstarter:in
371 Beiträge seit 2007
vor 15 Jahren

Es soll im folgenden nicht pValue als String sondern als pType gebunden werden:

public object property;


public void SetValue(string pValue,System.Type pType)
{
// das klappt logischer Weise nicht.
this.property = (pType)pValue; // oder pValue as pType
}

Besten Dank für Eure Hilfe antoschka

Gelöschter Account
vor 15 Jahren

solche casts müssen immer zur compilezeit festliegen.

generics aber können das

        public class c
        {
            public object property;


            public void SetValue<T>(T pValue)
            {
                // das klappt logischer Weise nicht.
                this.property = (T)pValue; // oder pValue as pType
            }    
        }
A
antoschka Themenstarter:in
371 Beiträge seit 2007
vor 15 Jahren

Besten Dank. Sorry ich hatte einen kleinen Fehler in meiner Beschreibung.
Den Type bekomme ich auch als string in der Methoden signatur. Heißt also:

public void SetValue(string pValue,string pType)
{...}

Wie bekommt man das dann hin.

Vielen Dank für Euer Feedback antoschka

Gelöschter Account
vor 15 Jahren

ist doch egal. der value ist ein string. daher ist der type schonmal uninteressant. oder meinst du etwa, das im type etwa sowas wie "System.Int32" kommen kann und im value "5" ?

A
antoschka Themenstarter:in
371 Beiträge seit 2007
vor 15 Jahren

Korrekt. Im pValue steht ein String der einen Font, in einen Farbewert, Int32 etc konvertiert werden soll und dann dem Property zugeweisen werden muss.
Ich habe die Möglichkiet pValue statt als string auch einem beliebigen anderen aber einheitlichen Format zu übergeben (binary?)
Irgendeine Idee?
Besten Dank antoschka

Gelöschter Account
vor 15 Jahren

nur zur klarheit:
also soll z.b. (string)"Arial" in z.b. (int)1234 convertiert werden?

A
antoschka Themenstarter:in
371 Beiträge seit 2007
vor 15 Jahren

sorry ich habe mich offensichtlich blöd ausgedrückt. Nochmal ich habe einen Wert mit Daten (bisher im string-format jedoch auch beliebig anders möglich - muss nur ein bestimmter Typ sein, da es aus einem Datenbank-Feld kommt) und ich habe zu jedem Datum einen Typ als string den ich mir aus einem anderen DB-Feld hole.

Ich habe also das Wertpaar "Arial", "System.Drawing.Font" oder "1234","System.Int64" und würde gern ein Property nicht mit den String Werten Arial oder 1234 füttern sondern mit den Font Arial bzw. der Long-Zahl 1234.
Wenn die Konvertierung des Strings in den Typ nicht klappt z.B. bei Wertpaaren
"1234", "System.Drawing.Font" oder "Arial","System.Int64" dann soll null zugewiesen werden.
Geht das irgendwie? Danke für Eure Hilfe antoschka

Gelöschter Account
vor 15 Jahren

factory-pattern

A
antoschka Themenstarter:in
371 Beiträge seit 2007
vor 15 Jahren

*heul* Ich habs gewusst. Ich kann das nicht. Kann mir jemand dabei helfen * schluchz*

Gelöschter Account
vor 15 Jahren

frag JuyJuka nach seinem microkernel. der ist genau das was du brauchst (soweit ich weiß).

A
antoschka Themenstarter:in
371 Beiträge seit 2007
vor 15 Jahren

besten Dank, werde ich tun! Könnte trotzdem jemand mir an diesem Bsp. abstarkt erklären, wie das Factory Pattern genutzt werden könnte.

lieben Dank Antoschka

5.941 Beiträge seit 2005
vor 15 Jahren

Salute JAck30lena

Wie soll da das Factory-Pattern (Welches überhaupt) oder ein Microkernel helfen?
Ich habe sowas mal für alle ValueTypes + Guid und ein paar weitere implementiert, nur fürs Web - also alle Typen die durch einen String dargestellt werden.

Sowas in der Art:


public static T GetTypedValue<T>(string value) {
    '// Implementation
}

Stichwörter: TypeConverter, Converter.ChangeType, IConvertible, Activator.CreateInstance, NullableType, ...

Gruss Peter

--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011

A
antoschka Themenstarter:in
371 Beiträge seit 2007
vor 15 Jahren

Die Methoden Signatur steht bei mir auch schon da, jedoch fehlt mir die Implementierung:

private T setTypedValue<T>(string pValue)
        {
            return (T)pValue;
        }

Das war ja ich was JAck30lena angedeutet hat. nur leider funktioniert es nicht - Fehler: string kann nicht in T konvertiert werden 🙁

Könntest Du mir Deine Implementierung schicken?

Besten Dank antoschka
P.S.: Ist es vielleicht einfacher mit einem anderen Typ für die Daten z.B. binary-Array?

Gelöschter Account
vor 15 Jahren

hallo Peter Bucher,

muss man beim typeconverter nciht für jeden typ eine separate unterscheidung machen. also sowas in der art:

                switch(valueTypeString)
                {
                    case "System.String":
                        property = value;
                        break;
                    case "System.Int32":
                        property = Int32.Parse(value);
                        break;
                        //usw...
                    default:
                        System.Diagnostics.Debug.Fail(
                            string.Format("Type '{0}' with value '{1}' not known", valueType, value));
                        property = null;
                        break;
                }

?

5.941 Beiträge seit 2005
vor 15 Jahren

Salute JAck30lena

Nö, wenn du mit Reflection arbeitest, nicht.
Bitte beantworte mir noch meine Fragen 🙂

Gruss Peter

--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011

A
antoschka Themenstarter:in
371 Beiträge seit 2007
vor 15 Jahren

Habe gerad ei BitConverter Klasse gefunden. Sagt mal wäre es nicht sinnvoller das DB-Feld meiner Daten als binary darzustellen und in c# byte[] zu verwenden. Dann könnte man mit der Klasse ziemlich viele Typen abdecken.
Besten Dank fürs Feedback antoschka

Gelöschter Account
vor 15 Jahren

Hallo Peter Bucher,

factory:
http://www.codeproject.com/KB/cs/genericfactorycs.aspx

microkernel:
ich dachte immer das microkernel dazu da sind instanzen von registrierten typen anzulegen.

Hallo antoschka,

wenn du die datenbank beeinflussen kannst, warum verwendest du dann nciht serialisierung?

A
antoschka Themenstarter:in
371 Beiträge seit 2007
vor 15 Jahren

Hallo JAck30lena,

Also ich habe ein Datenmodlel und einen OR-Mapper drüber (NHibernate). Prinzipiell sollte sich an dem Konzept nicht ändern. Zentrale Tabelle ist eine AttributTabelle, die ein ValueFeld hat. Dieses ist zur Zeit vom Typ string, kann aber auch von einem beliebig anderen Typ sein.

Unter Beibehaltung des KOnzeptes, wie würdest Du es denn am Besten anstellen?

Besten Dank fürs Feedback antoschka

X
1.177 Beiträge seit 2006
vor 15 Jahren

Moin,

"Type" kann übrigens auch einen "string" in den Typen zurückwandeln, nur dass ihr vor lauter Factory nicht vergesst.

🙂

Xynratron

Herr, schmeiss Hirn vom Himmel - Autsch!

Die Erfahrung zeigt immer wieder, dass viele Probleme sich in Luft auslösen, wenn man sich den nötigen Abstand bzw. Schlaf gönnt.

O
778 Beiträge seit 2007
vor 15 Jahren

man kann auch jedes mal das Rad mit Hilfe von Factorys neu erfinden.

Besser, standardkonformer und einfacher (weil für diverse Typen schon im Framework definiert) gehts aber mit Convertern:

TypeConverter c = TypeDescriptor.GetConverter(t);
if (c.CanConvertFrom(typeof(string)))
 return c.ConvertFrom(input);
else
 //Pech gehabt, geht nicht
 throw new NotSupportedException();

Ohne weiteres zutun werden jetzt schon Int16, Int32, Int32, Double, Decimal, sämtliche Enums, Boolean, DateTime, Point, Size, Rectangle u.v.m. unterstützt. Und wenn jetzt jemand seine ganz eigene Klasse, die du zur Compilezeit nicht kennst, erstellen will, dann brauch er nur einen TypeConverter schreiben und das sollte er eh tun, wenn es sinnvoll ist.

T
7 Beiträge seit 2008
vor 15 Jahren

in den meisten Fällen geht auch einfach:


var newObject = Convert.ChangeType(oldValue, targetType);

A
antoschka Themenstarter:in
371 Beiträge seit 2007
vor 15 Jahren

Leider bei mir nicht.

A
antoschka Themenstarter:in
371 Beiträge seit 2007
vor 15 Jahren

Also ich hab das jetzt mal in eine Methode verpackt die ich per Reflection aufrufe (da T nicht zur DesignTime bekannt ist)

public object setAttributeValueToString<T>(string pValue)
        {
if (typeof(T).Name == "String") 
                return nullString;
            
            TypeConverter converter = TypeDescriptor.GetConverter(typeof(T));
            if (converter.CanConvertFrom(typeof(string)))
                return converter.ConvertFrom(pValue == nullString ? default(T) : (object)pValue);
            else
            {
                //throw new NotSupportedException();
                return null;
            }
}

Jetzt erhalte ich beim Aufruf der Methode eine TargetInvocationException:_

MethodInfo methodInfo = this.GetType().GetMethod("setAttributeValueToString", new System.Type[] { typeof(string) });
myValue = methodInfo.MakeGenericMethod(currentType).Invoke(this, new object[] { currentAttributValue.AttributeValueOfNode.ValueString }); 

wieso kann den der FontConverter nicht mit default(T) - wobei T eben "Font" ist - umgehen?

Besten Dank fürs Feedback

49.485 Beiträge seit 2005
vor 15 Jahren

Hallo antoschka,

default (T) ist für Referenztypen null. Das wird also meistens unpassend sein.

Außerdem ist bei TargetInvocationException nur die InnerException interessant.

herbivore

5.941 Beiträge seit 2005
vor 15 Jahren

Hallo zusammen

@JAck30lena
Eine Factory bring nur was, wenn du den Typ oder den Typnamen als String zu einem Objekt bringen möchtest.
Zusätzlich kannst du auch gut Polymorphie nutzen.

Für diesen Fall ist das IMO aber nichts.

Beim Microkernel denke ich auch das gleiche, zugegeben aber ohne viel Ahnung davon zu haben.

Schlussendlich geht es ja nur darum Stringwerte in den gewünschten Typ zu parsen und den Typ dann sicher zurückzugeben, bei Fehlschlag dann ggf. default(T) / also 0 / null oder du benutzt Nullable.

Hier mal meine Implementation - wie gesagt noch nicht optimal und primär fürs Web gedacht.


using System;
using System.ComponentModel;
using System.Reflection;

namespace pb.Web
{
    public static partial class Tools
    {
        /// <summary>
        /// Gibt ein Typisiertes Objekt vom angegebenen Typ zurück.
        /// Unterstützt Value- und Referenztypen, sowie auch Nullable Typen.
        /// + Guid
        /// </summary>
        /// <param name="value">Objekt das typisiert werden soll</param>
        /// <returns>Typisiertes Objekt</returns>
        public static T GetTypedValue<T>(object value) {
            Type type = typeof(T); // gewünschter Typ
            Type baseType = null;          // gewünschter Untertyp (Wenn Nullable<>)
            bool isValueType;
            bool isNullable = false;

            // Wenn ein Nullable<> Objekt eingespiesen wird, den unterliegenden Typ holen
            if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) {
                // get underlying type (struct type)
                NullableConverter nc = new NullableConverter(type);
                baseType = nc.UnderlyingType;

                // Ist sowieso ein ValueType (Nullable geht nur auf ValueTypen)
                isValueType = true;
                isNullable = true;
            } else {
                // Checken ob Value- oder Referenztyp
                isValueType = type.IsValueType;
            }

            // Richtigen Typ auswählen (Nullable / Normal)
            type = baseType ?? type;

            //// Wenn möglich, mit System.Convert.ChangeType() behandeln
            //if(value is IConvertible) {
            //    IConvertible convertible = (IConvertible)value;
            //    return (T)Convert.ChangeType(value, type);
            //}

            IConvertible convertible = value as IConvertible;
            if (value != null && convertible != null) {
                try {
                    T result = (T)Convert.ChangeType(value, type);
                    return result;
                } catch (Exception e) { /* Unten gehts weiter... */
                }
            }

            // Methodeninfo holen, Signatur: void Parse(string source);
            MethodInfo method = type.GetMethod(
                                                    "Parse",
                                                    new Type[] { typeof(string) },
                                                    new ParameterModifier[] {
                                                                                 new ParameterModifier(1)
                                                                                });

            // Default Value setzen (Für Valuetypen bspw. int=0, bool=false, ...)
            T returnValue = default(T);
            if (method != null && isValueType) {
                try {
                    object parsedValue = method.Invoke(type,
                                                       new object[] { value.ToString() });

                    if (isValueType && isNullable) {
                        returnValue = (T)Activator.CreateInstance(typeof(T),
                                                                  new object[] { parsedValue });
                    } else {
                        returnValue = (T)parsedValue;
                    }
                } catch (Exception) {
                    // Wenn das Parsen fehlschlägt und wir einen Nullable Typ haben,
                    // eine leere Instanz (.HasValue = false) davon zurückgeben
                    if (isValueType && isNullable) {
                        returnValue = (T)Activator.CreateInstance(typeof(T));
                    }
                }
            } else {
                // Referenztypen gecastet zurückgaben (Bspw. Strings)
                // Guid wird ggf. geparst
                if (type.IsAssignableFrom(Guid.NewGuid().GetType())) {
                    GuidConverter gc = new GuidConverter();
                    T g;
                    try {
                        g = (T)gc.ConvertFromString(value.ToString());
                    } catch (Exception) {
                        g = (T)(object)Guid.Empty;
                    }

                    if (g != null)
                        return g;
                }

                returnValue = (T)value;
            }
            return returnValue;
        }
    }
}

Anwendung bspw.:


object test = "122";
int typed = Tools.GetTypedValue<int>(test); // typed = 122

object test2 = null;
int typed2 = Tools.GetTypedValue<int>(test2); // typed2 = 0

object nothing = null;
string nothingTyped = Tools.GetTypedValue<string>(nothing); // nothingTyped = String.Empty;

object test3 = "keineGuid";
Guid? typed3 = Tools.GetTypedValue<Guid?>(test3);
if(typed3.HasValue) {
    Guid guid = typed3.Value; // Gültige Guid
} else {
    // Keine gültige Guid
}

Ich sollte TryParse statt Parse verwenden, das ganze noch ein wenig vereinfachen und die Try / Catches minimieren.

Gruss Peter

--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011

A
antoschka Themenstarter:in
371 Beiträge seit 2007
vor 15 Jahren

Und wenn DU einen nicht-String-Type in String umwandeln möchtest, was nimmst Du dann? den einfachen TypeConverter?

Besten Dank fürs feedback antoschka

49.485 Beiträge seit 2005
vor 15 Jahren

Hallo antoschka,

Und wenn DU einen nicht-String-Type in String umwandeln möchtest, was nimmst Du dann?

.ToString ()

herbivore

A
antoschka Themenstarter:in
371 Beiträge seit 2007
vor 15 Jahren

Und darauf kann ich mich immer verlassen? Ich miene mich zu erinnern, dass es einige komische Standardimplementierungen für ToString gibt. Es kam jedenfalls imme rmal wieder im Forum vor.

antoschka

Gelöschter Account
vor 15 Jahren

bei vielen klassen bekommst du nur den typ mit dieser methode aber alle valuetypes liefern dir den inhalt selbst.