Laden...

Typkonvertierungen mit TypeConverter (Extension)

Erstellt von dr4g0n76 vor 10 Jahren Letzter Beitrag vor 7 Jahren 14.394 Views
dr4g0n76 Themenstarter:in
2.921 Beiträge seit 2005
vor 10 Jahren
Typkonvertierungen mit TypeConverter (Extension)

Mit untenstehender Klasse ist folgendes möglich:
(1. Version)

Arrays, Listen bzw. IEnumerables können konvertiert werden.
Von einem Typ in den anderen.
Selbst wenn die Klasse keinen entsprechenden AddRange-Befehl oder Konstruktor hat, ist das auch möglich.

Ich muss also nicht mehr schreiben:

Wenn z.B.


    //Complex conversions
            char[] a = new char[] { 'm', 'u', 'l', 't', 'u', 'm', 'e', 's', 'c' };
            byte[] b = new byte[] { 65, 66, 66, 65, 65, 66, 65, 66 };

            int[] resultIntArrayFromCharArray = (int[])typeof(int[]).Cast(a);
            int[] resultIntArrayFromByteArray = (int[])typeof(int[]).Cast(b);
            char[] resultCharArray = (char[])typeof(char[]).Cast(b);
            byte[] resultByteArray = (byte[])typeof(byte[]).Cast(a);
            //--------------------------------------------------------------------------
            //Convert from IEnumerable<Guid> to ObservableCollection<Guid>
            IEnumerable<Guid> guids = new Guid[] { Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid() };
            ObservableCollection<Guid> observableCollectionGuid = (ObservableCollection<Guid>)typeof(ObservableCollection<Guid>).Cast(guids);

            //Convert from ObservableCollection<Guid> to IEnumerable<Guid>
            ObservableCollection<Guid> observableCollectionGuid2 = new ObservableCollection<Guid>(new Guid[] { Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid() });
            IEnumerable<Guid> guids2 = (IEnumerable<Guid>)typeof(IEnumerable<Guid>).Cast(observableCollectionGuid2);

            //--------------------------------------------------------------------------
            //Simple conversion
            int intValue = 339;
            double? doubleValueCastedNullable = (double?)typeof(double?).Cast(intValue);

            intValue = 90943;
            double doubleValueCasted = (double)typeof(double).Cast(intValue); 

            double doubleValue = 7.0d;
            float floatValueCasted = (float)typeof(float).Cast(doubleValue);

            float floatValue = 3.09f;
            decimal decimalValueCasted = (decimal)typeof(decimal).Cast(floatValue);

            //--------------------------------------------------------------------------
            try
            {
                //at the moment an invalid cast exception is expected, when casting an array to one element
                string[] textValues = new string[] { "Hallo", "Hello", "Hola", "olá", "buna", "jak", "dag" };
                string sText = (string)typeof(string).Cast(textValues);
            }
            catch (InvalidCastException ex)
            {
                //so now we get an invalid cast
            }
            catch (Exception ex)
            {
            }

            //This will be casted, but at the moment makes no sense at all.
            //So one shouldn't do this.
            try
            {
                byte[] intValues = new byte[] { 7, 3, 9, 8, 5, 9 };
                int value = (int)typeof(int).Cast(intValue);
            }
            catch (InvalidCastException ex)
            {
            }
            catch (Exception ex)
            {
            }

            //--------------------------------------------------------------------------
            //This is possible: cast a single value to a list
            string textValue = "Hallo";
            List<string> sText2 = (List<string>)typeof(List<string>).Cast(textValue);

            //This is possible: cast a single value to a list
            Guid singleGuid = Guid.NewGuid();
            List<Guid> guidsOfSingleGuid = (List<Guid>)typeof(List<Guid>).Cast(singleGuid);

            intValue = 737;
            List<int> intsOfSingleInt = (List<int>)typeof(List<int>).Cast(intValue);

            bool trueValue = true;
            List<bool> boolsOfSingleBool = (List<bool>)typeof(List<bool>).Cast(trueValue);

         
            //--------------------------------------------------------------------------
            //This is also possible: Convert object to string: internally call object.ToString()
            intValue = 3457;
            string intValueCastedToString = (string)typeof(string).Cast(intValue);


using System;
using System.Runtime.CompilerServices;
using System.Collections;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using DynamicFramework.Iterators;

namespace DynamicFramework.TypeCast
{
    /// <summary>
    /// Use this class to do any type casts that would consume programming and code in one line.
    /// even casting like this is allowed:
    /// 
    /// 
    /// 
    /// </summary>
    public static class TypeCast
    {
        // This is the method exposing the rest of the functionality
        /// <summary>
        /// Casts the specified type.
        /// </summary>
        /// <param name="type">The type.</param>
        /// <param name="obj">The object.</param>
        /// <returns></returns>
        public static object Cast(this Type type, object obj)
        {
            return GetConverter(type, obj)(obj);
        }

        /// <summary>
        /// Converters contains the cached converters, 
        /// so using once it might be slow, the second time a cached converter
        /// will be used if possible.
        /// </summary>
        private static readonly IDictionary<PairOfTypes, Func<object, object>> converters =
            new Dictionary<PairOfTypes, Func<object, object>>();
        
        private static readonly ParameterExpression convParameter =
            Expression.Parameter(typeof(object), "val");

        /// <summary>
        /// Gets the converter.
        /// </summary>
        /// <param name="targetType">Type of the target.</param>
        /// <param name="val">The value.</param>
        /// <returns></returns>
        /// This is the method with the "guts" of the implementation
        [MethodImpl(MethodImplOptions.Synchronized)]
        private static Func<object, object> GetConverter(Type targetType, object val)
        {
            var fromType = val != null ? val.GetType() : typeof(object);
            var key = new PairOfTypes(fromType, targetType);
            Func<object, object> res;
            if (converters.TryGetValue(key, out res))
            {
                return res;
            }

            bool bConverted = true;
            try
            {
                res = (Func<object, object>)Expression.Lambda(
                    Expression.Convert(
                        Expression.Convert(
                            Expression.Convert(
                                convParameter
                            , fromType
                            )
                        , targetType
                        )
                    , typeof(object)
                    )
                , convParameter
                ).Compile();
                converters.Add(key, res);
            }
            catch (InvalidOperationException ex)
            {
                bConverted = false;
            }
            catch (Exception ex)
            {
                bConverted = false;
            }
            if (!bConverted)
            {
                IList listTargetType = null;
                IEnumerable listSourceType = val as IEnumerable;
                if (listSourceType != null)
                {
                    if (targetType.IsArray)
                    {

//TODO: for silverlight, what to do if there is an interface...
#if !SILVERLIGHT
                        if (listSourceType.GetType().GetInterface(typeof(IList).Name) != null)
                        {
                            IList list = (IList)listSourceType;
                            int count = list.Count;
                            listTargetType = (IList)Activator.CreateInstance(targetType, count);

                            int i = 0;
                            foreach (object oElement in Iter.All(listSourceType))
                            {
                                //unbox if needed, don't know yet, how to do by reflection
                                if (oElement.GetType() == typeof(char) && GetBaseTypeOfGenericIEnumerableType(listTargetType.GetType()) == typeof(byte))
                                {
                                    listTargetType[i++] = (byte)(char)oElement;
                                }
                                else
                                {
                                    listTargetType[i++] = oElement;
                                }
                            }
                        }
#endif
                    }
                    else
                    {
                        if (targetType == typeof(string))
                        {
                            throw new InvalidCastException(); //this is invalid, because one doesn't know how to convert an array into one element here.
                        }
                        listTargetType = (IList)Activator.CreateInstance(targetType);

                        if (val.GetType() == typeof(string))
                        {
                            listTargetType.Add(val);
                        }
                        else
                        {
                            foreach (object oElement in listSourceType)
                            {
                                listTargetType.Add(oElement);
                            }
                        }
                    }
                    //still null? consider case converting ONE element on source into list of elements in target
                }
                else
                {
                    if (targetType.FullName.Contains(val.GetType().FullName))
                    {
                        Type baseType = GetBaseTypeOfGenericIEnumerableType(targetType);
                        listTargetType = (IList)Activator.CreateInstance(targetType);
                        listTargetType.Add(val);
                    }
                }
                if (targetType == typeof(string) && listTargetType == null)
                {
                    res = (Func<object, object>)(x => val.ToString());
                    converters.Add(key, res);
                    return res;
                }
                else if (targetType.IsGenericType 
                    && listTargetType == null
                    && targetType.Name.Contains("List`1"))
                {
                    listTargetType = (IList)Activator.CreateInstance(targetType);
                    listTargetType.Add(val);
                }
                res = (Func<object, object>)(x => listTargetType); //this will get us what we want
                //converters.Add(key, res);
            }
            return res;
        }

        //TODO: avoid this method, if possible, but for the moment it's perfect.
        /// <summary>
        /// Found no other way to do this yet.
        /// </summary>
        /// <param name="targetType"></param>
        /// <returns></returns>
        public static Type GetBaseTypeOfGenericIEnumerableType(Type targetType)
        {
            string[] elements = System.Text.RegularExpressions.Regex.Split(targetType.FullName, @",|\[\[");
            if (elements.Length > 1)
            {
                return Type.GetType(elements[1]);
            }
            else
            {
                return Type.GetType(elements[0].TrimEnd(']').TrimEnd('['));
            }
        }
#if !FRAMEWORK35
        /// <summary>
        /// Converts the specified original.
        /// </summary>
        /// <param name="original">The original.</param>
        /// <returns></returns>
        public static ObservableCollection<object> Convert(IEnumerable original)
        {
            return new ObservableCollection<object>(original.Cast<object>());
        }

        /// <summary>
        /// Converts the specified original.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="original">The original.</param>
        /// <returns></returns>
        public static ObservableCollection<T> Convert<T>(IEnumerable<T> original)
        {
            return new ObservableCollection<T>(original);
        }

        /// <summary>
        /// Converts the specified original.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="original">The original.</param>
        /// <returns></returns>
        public static ObservableCollection<T> Convert<T>(IEnumerable original)
        {
            return new ObservableCollection<T>(original.Cast<T>());
        }
#endif
        
        /// <summary>
        /// Find out how to really unbox!!!
        /// </summary>
        /// <param name="oElement"></param>
        /// <param name="sourceType"></param>
        /// <param name="destinationType"></param>
        /// <returns></returns>
        private static object Unbox(object oElement, Type sourceType, Type destinationType)
        {
            if (sourceType == typeof(char) && destinationType == typeof(byte))
            {
                oElement = (byte)(char)oElement;
            }
            return oElement;
        }

        // This class provides Equals and GetHashCode
        // for a pair of System.Type objects.
        private class PairOfTypes
        {
            private readonly Type first;
            private readonly Type second;
            public PairOfTypes(Type first, Type second)
            {
                this.first = first;
                this.second = second;
            }
            public override int GetHashCode()
            {
                return 31 * first.GetHashCode() + second.GetHashCode();
            }
            public override bool Equals(object obj)
            {
                if (obj == this)
                {
                    return true;
                }
                var other = obj as PairOfTypes;
                if (other == null)
                {
                    return false;
                }
                return first.Equals(other.first)
                    && second.Equals(other.second);
            }
        }
    }
}

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

dr4g0n76 Themenstarter:in
2.921 Beiträge seit 2005
vor 9 Jahren

Anmerkung:

Gerade erst bemerkt.
Die Klasse benutzt einen Namespace der sich DynamicFramework.Iterators nennt.

Das ist nichts anderes als die komplett unveränderte Lösung für Iteratoren von Herbivore auf:

Hilfreiche Iteratoren / Improving Foreach

EDIT: Damit lässt sich die Klasse benutzen und fehlerfrei kompilieren.

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

dr4g0n76 Themenstarter:in
2.921 Beiträge seit 2005
vor 9 Jahren

Ich habe ein Beispiel-Projekt angehängt im obersten Post, dann kann jeder ausprobieren und muss nicht erst beide Klassen in ein Projekt kopieren.

Auf jede der Funktionen die über die entsprechenden Buttons der Form gesetzt sind, sind Breakpoints gesetzt, so dass sich jeder in Ruhe anschauen kann was passiert.

Ich editiere den oberen Beitrag nochmals, so dass einige Dinge etwas klarer werden sollten.

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

dr4g0n76 Themenstarter:in
2.921 Beiträge seit 2005
vor 7 Jahren

Hat irgendjemand zufällig eine Idee, wie man bei

List<Guid> guidsOfSingleGuid = (List<Guid>)typeof(List<Guid>).Cast(singleGuid);

um das

(List<Guid>)typeof(List<Guid>).Cast(singleGuid);

herumkommt?

Also dass man statt (List<Guid>)typeof(List<Guid>).Cast(singleGuid);
quasi was ganz kurzes schreiben könnte, was eben näher an:

List<Guid> guidsOfSingleGuid = singleGuid.CastTo(List<Guid>);

ist?

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

5.657 Beiträge seit 2006
vor 7 Jahren

Hi dr4g0n76,

meinst du soetwas:


public static IEnumerable<T> ToEnumerable<T>(this T item)
{
  yield return item;
}


List<Guid> guidsOfSingleGuid = singleGuid.ToEnumerable().ToList();

Weeks of programming can save you hours of planning

4.931 Beiträge seit 2008
vor 7 Jahren

Hallo dr4g0n76,

wenn dann wohl


List<Guid> guidsOfSingleGuid = singleGuid.CastTo<List<Guid>>();

denn sonst müßtest du ja


List<Guid> guidsOfSingleGuid = singleGuid.CastTo(typeof(List<Guid>));

schreiben.

Und das geht dann per


public static T CastTo(this Guid guid)
{
    return (T)typeof(T).Cast(guid);
}

Wenn T aber immer List<Guid> ist, kannst du es natürlich vereinfachen (spezialisieren):


public static List<Guid> CastTo(this Guid guid)
{
    return (List<Guid>)typeof(List<Guid>).Cast(guid);
}

(Extension-Methoden müssen dann natürlich in eine eigene statische Klasse z.B. GuidExtensions)