70

拡張メソッドで可能だと聞いたことがありますが、自分ではよくわかりません。できれば具体例を示していただきたいです。

ありがとう!

4

9 に答える 9

100

それは、「ミックスイン」が何を意味するかによって大きく異なります。誰もが少し異なる考えを持っているようです。が見たい種類の mixin (ただし、C# では利用できません) は、コンポジションによる実装をシンプルにします。

public class Mixin : ISomeInterface
{
    private SomeImplementation impl implements ISomeInterface;

    public void OneMethod()
    {
        // Specialise just this method
    }
}

コンパイラは、クラスに直接別の実装がない限り、すべてのメンバーを「impl」にプロキシするだけで ISomeInterface を実装します。

ただし、現時点ではこれは不可能です:)

于 2008-11-01T07:30:56.600 に答える
10

C# 経由でミックスインを実装できるオープン ソース フレームワークがあります。http://remix.codeplex.com/をご覧ください。

このフレームワークでミックスインを実装するのは非常に簡単です。サンプルと、ページにある「追加情報」リンクをご覧ください。

于 2011-01-12T09:24:58.690 に答える
4

似たようなものが必要だったので、Reflection.Emit を使用して次のように考えました。次のコードでは、「mixin」型のプライベート メンバーを持つ新しい型が動的に生成されます。「mixin」インターフェースのメソッドへのすべての呼び出しは、このプライベート メンバーに転送されます。「mixin」インターフェースを実装するインスタンスを取る単一のパラメーターコンストラクターが定義されています。基本的には、特定の具体的な型 T とインターフェイス I に対して次のコードを記述することと同じです。

class Z : T, I
{
    I impl;

    public Z(I impl)
    {
        this.impl = impl;
    }

    // Implement all methods of I by proxying them through this.impl
    // as follows: 
    //
    // I.Foo()
    // {
    //    return this.impl.Foo();
    // }
}

これはクラスです:

public class MixinGenerator
{
    public static Type CreateMixin(Type @base, Type mixin)
    {
        // Mixin must be an interface
        if (!mixin.IsInterface)
            throw new ArgumentException("mixin not an interface");

        TypeBuilder typeBuilder = DefineType(@base, new Type[]{mixin});

        FieldBuilder fb = typeBuilder.DefineField("impl", mixin, FieldAttributes.Private);

        DefineConstructor(typeBuilder, fb);

        DefineInterfaceMethods(typeBuilder, mixin, fb);

        Type t = typeBuilder.CreateType();

        return t;
    }

    static AssemblyBuilder assemblyBuilder;
    private static TypeBuilder DefineType(Type @base, Type [] interfaces)
    {
        assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
            new AssemblyName(Guid.NewGuid().ToString()), AssemblyBuilderAccess.RunAndSave);

        ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(Guid.NewGuid().ToString());

        TypeBuilder b = moduleBuilder.DefineType(Guid.NewGuid().ToString(),
            @base.Attributes,
            @base,
            interfaces);

        return b;
    }
    private static void DefineConstructor(TypeBuilder typeBuilder, FieldBuilder fieldBuilder)
    {
        ConstructorBuilder ctor = typeBuilder.DefineConstructor(
            MethodAttributes.Public, CallingConventions.Standard, new Type[] { fieldBuilder.FieldType });

        ILGenerator il = ctor.GetILGenerator();

        // Call base constructor
        ConstructorInfo baseCtorInfo =  typeBuilder.BaseType.GetConstructor(new Type[]{});
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Call, typeBuilder.BaseType.GetConstructor(new Type[0]));

        // Store type parameter in private field
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Ldarg_1);
        il.Emit(OpCodes.Stfld, fieldBuilder);
        il.Emit(OpCodes.Ret);
    }

    private static void DefineInterfaceMethods(TypeBuilder typeBuilder, Type mixin, FieldInfo instanceField)
    {
        MethodInfo[] methods = mixin.GetMethods();

        foreach (MethodInfo method in methods)
        {
            MethodInfo fwdMethod = instanceField.FieldType.GetMethod(method.Name,
                method.GetParameters().Select((pi) => { return pi.ParameterType; }).ToArray<Type>());

            MethodBuilder methodBuilder = typeBuilder.DefineMethod(
                                            fwdMethod.Name,
                                            // Could not call absract method, so remove flag
                                            fwdMethod.Attributes & (~MethodAttributes.Abstract),
                                            fwdMethod.ReturnType,
                                            fwdMethod.GetParameters().Select(p => p.ParameterType).ToArray());

            methodBuilder.SetReturnType(method.ReturnType);
            typeBuilder.DefineMethodOverride(methodBuilder, method);

            // Emit method body
            ILGenerator il = methodBuilder.GetILGenerator();
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldfld, instanceField);

            // Call with same parameters
            for (int i = 0; i < method.GetParameters().Length; i++)
            {
                il.Emit(OpCodes.Ldarg, i + 1);
            }
            il.Emit(OpCodes.Call, fwdMethod);
            il.Emit(OpCodes.Ret);
        }
    }
}

これは使用法です:

public interface ISum
{
    int Sum(int x, int y);
}

public class SumImpl : ISum
{
    public int Sum(int x, int y)
    {
        return x + y;
    }
}

public class Multiply
{        
    public int Mul(int x, int y)
    {
        return x * y;
    }
}

// Generate a type that does multiply and sum
Type newType = MixinGenerator.CreateMixin(typeof(Multiply), typeof(ISum));

object instance = Activator.CreateInstance(newType, new object[] { new SumImpl() });

int res = ((Multiply)instance).Mul(2, 4);
Console.WriteLine(res);
res = ((ISum)instance).Sum(1, 4);
Console.WriteLine(res);
于 2015-02-21T23:00:24.747 に答える
4

LinFuCastle の DynamicProxyは mixin を実装しています。COP (複合指向プログラミング) は、ミックスインからパラダイム全体を作成するものと見なすことができます。Anders Noras のこの投稿には、役立つ情報とリンクがあります。

編集:これは、拡張メソッドなしで、C# 2.0 ですべて可能です。

于 2008-11-01T15:44:32.860 に答える
3

また、拡張メソッドのアプローチを拡張して、WPFのアタッチされたプロパティと同じパターンで状態を組み込むこともできます。

これは、最小のボイラープレートを使用した例です。ターゲットクラスを多態的に処理する必要がない限り、インターフェイスの追加など、ターゲットクラスを変更する必要がないことに注意してください。その場合、実際の多重継承に非常に近いものになります。

// Mixin class: mixin infrastructure and mixin component definitions
public static class Mixin
{ 
    // =====================================
    // ComponentFoo: Sample mixin component
    // =====================================

    //  ComponentFooState: ComponentFoo contents
    class ComponentFooState
    {
        public ComponentFooState() {
            // initialize as you like
            this.Name = "default name";
        }

        public string Name { get; set; }
    }

    // ComponentFoo methods

    // if you like, replace T with some interface 
    // implemented by your target class(es)

    public static void 
    SetName<T>(this T obj, string name) {
        var state = GetState(component_foo_states, obj);

        // do something with "obj" and "state"
        // for example: 

        state.Name = name + " the " + obj.GetType();


    }
    public static string
    GetName<T>(this T obj) {
        var state = GetState(component_foo_states, obj);

        return state.Name; 
    }

    // =====================================
    // boilerplate
    // =====================================

    //  instances of ComponentFoo's state container class,
    //  indexed by target object
    static readonly Dictionary<object, ComponentFooState>
    component_foo_states = new Dictionary<object, ComponentFooState>();

    // get a target class object's associated state
    // note lazy instantiation
    static TState
    GetState<TState>(Dictionary<object, TState> dict, object obj) 
    where TState : new() {
        TState ret;
        if(!dict.TryGet(obj, out ret))
            dict[obj] = ret = new TState();

        return ret;
    }

}

使用法:

var some_obj = new SomeClass();
some_obj.SetName("Johny");
Console.WriteLine(some_obj.GetName()); // "Johny the SomeClass"

拡張メソッドは自然に機能するため、nullインスタンスでも機能することに注意してください。

また、WeakDictionary実装を使用して、コレクションがターゲットクラス参照をキーとして保持することによって引き起こされるメモリリークを回避することを検討することもできます。

于 2012-08-03T16:03:48.267 に答える
1

データを格納できる基本クラスがある場合は、コンパイラの安全性を強化し、マーカー インターフェイスを使用できます。それは多かれ少なかれ、受け入れられた回答の「Mixins in C# 3.0」が提案するものです。

public static class ModelBaseMixins
{
    public interface IHasStuff{ }

    public static void AddStuff<TObjectBase>(this TObjectBase objectBase, Stuff stuff) where TObjectBase: ObjectBase, IHasStuff
    {
        var stuffStore = objectBase.Get<IList<Stuff>>("stuffStore");
        stuffStore.Add(stuff);
    }
}

オブジェクトベース:

public abstract class ObjectBase
{
    protected ModelBase()
    {
        _objects = new Dictionary<string, object>();
    }

    private readonly Dictionary<string, object> _objects;

    internal void Add<T>(T thing, string name)
    {
        _objects[name] = thing;
    }

    internal T Get<T>(string name)
    {
        T thing = null;
        _objects.TryGetValue(name, out thing);

        return (T) thing;
    }

したがって、「ObjectBase」から継承して IHasStuff で装飾できるクラスがある場合は、sutff を追加できます。

于 2015-05-28T10:08:57.807 に答える