Dynamically implementing C# interfaces possible or not

It is possible dynamically implement an interface in C# without emitting code using reflection, maybe with the help of DLR.

I mean create a factory that allow us to do the following:

      IComparable comparable = factory.Create<IComparable>();

Either Roslyn way.

Upvotes: 2

Views: 2931

Answers (2)

Martin Liversage
Martin Liversage

Reputation: 106816

It is possible dynamically implement an interface in C# without emitting code using reflection, maybe with the help of DLR.

Yes, I would think so. Bill Wagner has an article named Implementing Dynamic Interfaces. It demonstrates how you can use IDynamicMetaObjectProvide interface and in particular the DynamicMetaObject class to roll you very own dynamic object. However, it is a somewhat complex concept and at some point it will require some reflection to hook up the members of the interface to the corresponding DLR representation.

You cannot use an already existing dynamic object like ExpandoObject because the implemented interfaces (actually just one interface) cannot be changed dynamically.

Upvotes: 0

Louis Kottmann
Louis Kottmann

Reputation: 16618

I may be wrong, but it you seem to be looking for Mixins.

They are not officially supported in C#, but fortunately there's the re-mix project (part of re-motion)

I've used it in the past and it works well, here's an example:

/// <summary>
    /// This <see cref="Mixin"/> is used to "automatically" implement <see cref="INotifyPropertyChanged"/> to a target class.
    /// <para>It will also override <c>ToString()</c> to show it's possible.</para>
    /// </summary>
    /// <example>This example adds <see cref="INotifyPropertyChanged"/> to <see cref="INPCTester"/> 
    /// <code>
    /// [ImplementsINPC]
    /// public class INPCTester
    /// {
    ///     private string m_Name;
    ///     public string Name
    ///     {
    ///         get { return m_Name; }
    ///         set
    ///         {
    ///             if (m_Name != value)
    ///             {
    ///                 m_Name = value;
    ///                 ((ICustomINPC)this).RaisePropertyChanged("Name");
    ///             }
    ///         }
    ///     }
    /// }
    /// 
    /// class Program
    /// {
    ///     static void Main(string[] args)
    ///     {
    ///         INPCImplementation();
    ///     }
    ///     
    ///     static void INPCImplementation()
    ///     {
    ///         Console.WriteLine("INPC implementation and usage");
    ///
    ///         var inpc = ObjectFactory.Create{INPCTester}(ParamList.Empty);
    ///
    ///         Console.WriteLine("The resulting object is castable as INPC: " + (inpc is INotifyPropertyChanged));
    ///
    ///         ((INotifyPropertyChanged)inpc).PropertyChanged += inpc_PropertyChanged;
    ///
    ///         inpc.Name = "New name!";
    ///         Console.WriteLine(inpc.ToString());
    ///         ((INotifyPropertyChanged)inpc).PropertyChanged -= inpc_PropertyChanged;
    ///         Console.WriteLine();
    ///     }
    /// }
    /// 
    /// //Outputs: 
    /// //
    /// //INPC implementation and usage
    /// //The resulting object is castable as INPC: True
    /// //Hello, world! Property's name: Name
    /// //Modified tostring!
    /// </code>
    /// </example>    
    /// <remarks>
    /// The <see cref="ImplementsINPCAttribute"/> is syntactic sugar for 
    /// <para>   <c>[Uses(typeof(INotifyPropertyChangedMixin))]</c> on top of the target class</para>
    /// <para>Which is equivalent to: </para>
    /// <para>   <c>[assembly: Mix(typeof(INPCTester), typeof(INotifyPropertyChangedMixin))]</c> outside the namespace.</para>
    /// <para>or <c>[Extends(typeof(INPCTester))]</c> on top of the mixin class</para>
    /// </remarks>
    public class INotifyPropertyChangedMixin : Mixin<object>, ICustomINPC
    {
        public event PropertyChangedEventHandler PropertyChanged;

        /// <inheritdoc />
        public void RaisePropertyChanged(string prop)
        {
             PropertyChangedEventHandler handler = this.PropertyChanged;
             if (handler != null)
             {
                 handler(this, new PropertyChangedEventArgs(prop));
             }
        }

        /// <inheritdoc />
        [OverrideTarget]
        public new string ToString()
        {
            return "Modified tostring!";
        }
    }

    public class ImplementsINPCAttribute : UsesAttribute 
    {
        public ImplementsINPCAttribute()
            : base(typeof(INotifyPropertyChangedMixin))
        {

        }
    }

Please notice that while the INPCTester class does not implement INotifyPropertyChanged, it can be casted to it and treated as if it did.

Advanced usage allows you to modify types for newly created objects during the lifetime of the application.

Upvotes: 1

Related Questions