Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

  • »
  • Community
  • |
  • Diskussionsforum
Interface zur Laufzeit zu einer Klasse hinzufügen (Lösung)
dr4g0n76
myCSharp.de - Experte

Avatar #avatar-1768.jpg


Dabei seit:
Beiträge: 2.908
Herkunft: Deutschland

Themenstarter:

Interface zur Laufzeit zu einer Klasse hinzufügen (Lösung)

beantworten | zitieren | melden

Das ist nur ein Versuch.

Neulich kam mir die interessante Idee, ob es wohl ohne Aspekt-Orientierte-Programmierung, einfach nur mit den Standardmitteln von C# wohl möglich ist, zur Laufzeit ein Interface in eine Klasse zu implementieren.
Dabei ist folgender Code entstanden, der IMHO sehr gut funktioniert.

Habe mich dazu auch ein einigen Beispieln im Internet orientiert, die sich mit den Klassen System.Reflection und System.Reflection.Emit befassen.


using System;
using System.Data;
using System.Windows.Forms;

using System.Threading;
using System.Reflection;
using System.Reflection.Emit;
using System.Diagnostics;
using System.Collections.Generic;

namespace Library.ExtendedBaseTypes.Generics
{
    /// <summary> 
    /// This is the base class for the automatically generated wrappers. 
    /// </summary> 
    public class WrapperBase
    {
        internal protected object m_oSource = null;

        internal object NewFromPrototype(object _source)
        {
            WrapperBase newWrapper = (WrapperBase)MemberwiseClone();
            newWrapper.m_oSource = _source;
            return newWrapper;
        }
    }

    /// <summary> 
    /// This internal class generates a wrapper type from one type to an interface 
    /// the type does not implement. 
    /// </summary> 
    internal class WrapperGenerator
    {
        static AppDomain m_AppDomain;
        static AssemblyBuilder m_AssemblyBuilder;
        static ModuleBuilder m_ProxyModule;

        static WrapperGenerator()
        {
            m_AppDomain = Thread.GetDomain();
            m_AssemblyBuilder = m_AppDomain.DefineDynamicAssembly(new AssemblyName("Wrappers"),AssemblyBuilderAccess.Run);
            m_ProxyModule = m_AssemblyBuilder.DefineDynamicModule("WrapperModule", false);
        }

        /// <summary> 
        /// Generates a wrapper type using System.Reflection.Emit. 
        /// </summary> 
        /// <param name="tgt">The target type. Must be an interface</param> 
        /// <param name="src">The source type. Must contain all methods required for implementing the target type, but must not actually implement the target type.</param> 
        /// <returns>The wrapper type</returns> 
        /// <exception cref="System.MissingMethodException">if <paramref name="src"/> does not contain all required methods to implement <paramref name="dst"/></exception> 
        public static Type GenerateWrapperType(Type _targetType, Type _source)
        {
            Trace.Assert(_targetType.IsInterface);
            TypeBuilder proxyBuilder = m_ProxyModule.DefineType(
                _source.Name + "To" + _targetType.Name + "Wrapper",
                TypeAttributes.NotPublic | TypeAttributes.Sealed,
                typeof(WrapperBase),
                new Type[] { _targetType });

            FieldInfo srcField = typeof(WrapperBase).GetField("m_oSource", BindingFlags.Instance | BindingFlags.NonPublic);

            foreach (MethodInfo method in _targetType.GetMethods())
            {
                ParameterInfo[] aParameter = method.GetParameters();
                Type[] aParameterType = new Type[aParameter.Length];
                
                for (int i = 0; i < aParameter.Length; i++)
                {
                    aParameterType[i] = aParameter[i].ParameterType;
                }

                MethodBuilder methodBuilder = proxyBuilder.DefineMethod(method.Name,
                    MethodAttributes.Public | MethodAttributes.Virtual,
                    method.ReturnType, aParameterType);

                ILGenerator ilGenerator = methodBuilder.GetILGenerator();
                ilGenerator.Emit(OpCodes.Ldarg_0);  //load this                 
                ilGenerator.Emit(OpCodes.Ldfld, srcField); //load this->src 
                //push parameters on the stack 
                for (int i = 1; i < aParameter.Length + 1; i++)
                {
                    ilGenerator.Emit(OpCodes.Ldarg, i);
                }
                
                MethodInfo sourceMethod = _source.GetMethod(method.Name, aParameterType);
                
                if (sourceMethod == null)
                    throw new MissingMethodException("The source type " + _source.FullName + " does not contain the " + method.Name + " method " +
                        "required to implement the " + _targetType.FullName + " interface!");

                //call (virtual if nessecary) 
                if (method.IsVirtual)
                {
                    ilGenerator.Emit(OpCodes.Callvirt, sourceMethod);
                }
                else
                {
                    ilGenerator.Emit(OpCodes.Call, sourceMethod);
                }
                ilGenerator.Emit(OpCodes.Ret); //return
            }
            return proxyBuilder.CreateType();
        }

        public static WrapperBase GenerateWrapperPrototype(Type _targetType, Type _sourceType)
        {
            // emit a wrapper type to wrap T as implementing I. 
            // This is where an exception might be thrown. 
            Type wrapperType = GenerateWrapperType(_targetType, _sourceType);
            // create a prototype object because Activator.CreateInstance 
            // is too slow to use it every time. 
            return (WrapperBase)Activator.CreateInstance(wrapperType);
        }
    }

    internal sealed class Latent<I, T>
    {
        static WrapperBase wrapperPrototype;
        static Latent()
        {
            wrapperPrototype = WrapperGenerator.GenerateWrapperPrototype(typeof(I), typeof(T));
        }
        public static I Cast(T src)
        {
            return (I)wrapperPrototype.NewFromPrototype(src);
        }
    }

    /// <summary> 
    /// A class that automatically generates wrapper classes for types that 
    /// implement all required methods for an interface, but do not explicitly 
    /// implement the interface. For example if you have an object <code>a</code> 
    /// of a type that contains a <code>int CompareTo(object o);</code> method but 
    /// does not implement the <code>IComparable</code> interface, you could 
    /// automatically create a wrapper using 
    /// <code></code> 
    /// or 
    /// <code></code> 
    /// </summary> 
    /// <typeparam name="I">The interface to create wrappers for.</typeparam> 
    public sealed class Latent<I>
    {
        private static Dictionary<Type, WrapperBase> wrapperPrototypes = null;
        private static WrapperBase GetWrapperPrototype(Type type)
        {
            if (wrapperPrototypes == null)
                wrapperPrototypes = new Dictionary<Type, WrapperBase>();
            if (wrapperPrototypes.ContainsKey(type))
                return wrapperPrototypes[type];
            else
                return wrapperPrototypes[type] = WrapperGenerator.GenerateWrapperPrototype(typeof(I), type);
        }

        /// <summary> 
        /// The dynamic cast method. Generates a wrapper if necessary, so the first invocation 
        /// for a new source type will take more time than subsequent invocations. 
        /// </summary> 
        /// <param name="src">The object to wrap.</param> 
        /// <returns>The wrapper, conveniently casted to <typeparamref name="I"/>.</returns> 
        /// <exception cref="System.MissingMethodException">if <paramref name="src"/> does not contain all required methods to implement <typeparamref name="I"/></exception> 
        public static I CastObject(object _oSource)
        {
            if (typeof(I).IsAssignableFrom(_oSource.GetType()))
                return (I)_oSource;
            else
                return (I)GetWrapperPrototype(_oSource.GetType()).NewFromPrototype(_oSource);
        }

        /// <summary> 
        /// The static cast method. Generates a wrapper if nessecary, so the first invocation 
        /// for a new source type will take more time than subsequent invocations. 
        /// </summary> 
        /// <param name="src">The object to wrap.</param> 
        /// <typeparam name="T">The type of the object to wrap</typeparam> 
        /// <returns>The wrapper, conveniently casted to <typeparamref name="I"/>.</returns> 
        /// <exception cref="System.MissingMethodException">if <paramref name="src"/> does not contain all required methods to implement <typeparamref name="I"/></exception> 
        public static I Cast<T>(T src)
        {
            return Latent<I, T>.Cast(src);
        }
    }

Jetzt könnten wir zum Beispiel folgendes schreiben:

    /// <summary> 
    /// This is a test interface 
    /// </summary> 
    public interface ITest
    {
        void DoIt();
        void Print(string a, string b);
        void Print(int x, int y);
        int Sum(int x, int y);
    }

    /// <summary> 
    /// This is a class that implements all required methods for the above 
    /// interface ITest, but does not explicitly implement the ITest interface. 
    /// </summary> 
    public class Test
    {
        //no parameters at all 
        public void DoIt()
        {
            System.Console.WriteLine("DoIt()");
        }
        //normal reference types 
        public void Print(string a, string b)
        {
            System.Console.WriteLine("Print({0},{1})", a, b);
        }
        //primitive types 
        public void Print(int i, int j)
        {
            System.Console.WriteLine("Print({0},{1})", i, j);
        }
        //return primitive types 
        public int Sum(int i, int j)
        {
            return i + j;
        }
    }
}

  class TestMain 
    { 
        public static void Main() 
        { 
            try 
            { 
                Test test = new Test(); 
                ITest itest = Latent<ITest>.Cast(test); //Diese Zeile hier ist wichtig
                itest.DoIt(); 
                itest.Print("Hello", "World"); 
                itest.Print(1, 2); 
                Console.WriteLine(itest.Sum(1, 2)); 
            } 
            catch (Exception e) 
            { 
                Console.WriteLine(e); 
                Console.WriteLine(e.StackTrace); 
            } 
        } 
    } 


In der markierten Zeile wird jetzt der obige Mechanismus angeworfen, die Dynamische Cast Methode generiert automatisch einen Wrapper falls benötigt.

Alles was dafür benötigt wird, wird automatisch mitkopiert.
Und wir erhalten am Ende ein Interface das automatisch richtig implementiert wird.
Jetzt können wir ganz normal die Funktionalität von "Test" über das Interface "ITest" aufrufen.
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von dr4g0n76 am .
Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.
private Nachricht | Beiträge des Benutzers
dr4g0n76
myCSharp.de - Experte

Avatar #avatar-1768.jpg


Dabei seit:
Beiträge: 2.908
Herkunft: Deutschland

Themenstarter:

beantworten | zitieren | melden

Siehe auch (als Ergänzung):

Lösung für: Anonymen Typ in ein Interface casten
Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.
private Nachricht | Beiträge des Benutzers
Palladin007
myCSharp.de - Member

Avatar #avatar-4140.png


Dabei seit:
Beiträge: 1.803
Herkunft: Düsseldorf

beantworten | zitieren | melden

System.Reflection.DispatchProxy
Hab ich selber nie genutzt, klingt aber vergleichbar.
private Nachricht | Beiträge des Benutzers