web-dev-qa-db-ja.com

属性パラメーターとしてデリゲートを持つことは可能ですか?

属性のパラメーターとしてデリゲートを持つことは可能ですか?

このような:

public delegate IPropertySet ConnectionPropertiesDelegate();

public static class TestDelegate
{
    public static IPropertySet GetConnection()
    {
        return new PropertySetClass();
    }
}

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface,AllowMultiple=false,Inherited=true)]
public class WorkspaceAttribute : Attribute
{
    public ConnectionPropertiesDelegate ConnectionDelegate { get; set; }

    public WorkspaceAttribute(ConnectionPropertiesDelegate connectionDelegate)
    {
        ConnectionDelegate = connectionDelegate;
    }
}

[Workspace(TestDelegate.GetConnection)]
public class Test
{
}

そしてそれが不可能な場合、賢明な代替案は何ですか?

43
George Silva

いいえ、デリゲートを属性コンストラクタパラメータとして使用することはできません。利用可能なタイプを参照してください: 属性パラメーターのタイプ
回避策として(ハックでエラーが発生しやすくなりますが) リフレクション付きデリゲート を作成できます。

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false, Inherited = true)]
public class WorkspaceAttribute : Attribute
{
    public ConnectionPropertiesDelegate ConnectionDelegate { get; set; }

    public WorkspaceAttribute(Type delegateType, string delegateName)
    {
         ConnectionDelegate = (ConnectionPropertiesDelegate)Delegate.CreateDelegate(typeof(ConnectionPropertiesDelegate), delegateType, delegateName);
    }
}

[Workspace(typeof(TestDelegate), "GetConnection")]
public class Test
{
}
39
nemesv

その他の考えられる回避策は、デリゲート定義に一致する抽象メソッドで抽象基本属性タイプを作成し、そのメソッドを具象属性クラスに実装することです。

次の利点があります。

  • 注釈はより簡潔で簡潔です(DSLのような)
  • 反射なし
  • 再利用が簡単

例:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple=false, Inherited=true)]
public abstract class GetConnectionAttribute : Attribute
{
    public abstract IPropertySet GetConnection();
}

public class GetConnectionFromPropertySetAttribute : GetConnectionAttribute
{
    public override IPropertySet GetConnection()
    {
        return new PropertySetClass();
    }
}

[GetConnectionFromPropertySet]
public class Test
{
}
14
dadhi

私は、列挙型とデリゲートのマッピング配列を使用してこれを解決しました。継承を使用するアイデアは本当に気に入っていますが、私のシナリオでは、比較的単純なことを行うためにいくつかの子クラスを記述する必要があります。これもリファクタリングできるはずです。唯一の欠点は、配列内のデリゲートのインデックスを列挙値に対応させる必要があることです。

public delegate string FormatterFunc(string val);

public enum Formatter
{
    None,
    PhoneNumberFormatter
}

public static readonly FormatterFunc[] FormatterMappings = { null, PhoneNumberFormatter };

public string SomeFunction(string zzz)
{
   //The property in the attribute is named CustomFormatter
   return FormatterMappings[(int)YourAttributeHere.CustomFormatter](zzz);
}
1
Jeff Sall

ネクロマンシング。
動的デリゲートタイプを使用するために受け入れられた回答を拡張しました:

namespace NetStandardReporting
{


    // [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false, Inherited = true)]
    public class DynamicDllImportAttribute
        : System.Attribute
    {
        protected string m_name;


        public string Name
        {
            get
            {
                return this.m_name;
            }
        }


        public DynamicDllImportAttribute(string name)
            : base()
        {
            this.m_name = name;
        }


        private static System.Type CreateDelegateType(System.Reflection.MethodInfo methodInfo)
        {
            System.Func<System.Type[], System.Type> getType;
            bool isAction = methodInfo.ReturnType.Equals((typeof(void)));

            System.Reflection.ParameterInfo[] pis = methodInfo.GetParameters();
            System.Type[] types = new System.Type[pis.Length + (isAction ? 0: 1)];

            for (int i = 0; i < pis.Length; ++i)
            {
                types[i] = pis[i].ParameterType;
            }

            if (isAction)
            {
                getType = System.Linq.Expressions.Expression.GetActionType;
            }
            else
            {
                getType = System.Linq.Expressions.Expression.GetFuncType;
                types[pis.Length] = methodInfo.ReturnType;
            }

            return getType(types);
        }


        private static System.Delegate CreateDelegate(System.Reflection.MethodInfo methodInfo, object target)
        {
            System.Type tDelegate = CreateDelegateType(methodInfo);

            if(target != null)
                return System.Delegate.CreateDelegate(tDelegate, target, methodInfo.Name);

            return System.Delegate.CreateDelegate(tDelegate, methodInfo);
        }


        // protected delegate string getName_t();

        public DynamicDllImportAttribute(System.Type classType, string delegateName)
            : base()
        {
            System.Reflection.MethodInfo mi = classType.GetMethod(delegateName,
                  System.Reflection.BindingFlags.Static
                | System.Reflection.BindingFlags.Public
                | System.Reflection.BindingFlags.NonPublic
            );

            // getName_t getName = (getName_t)System.Delegate.CreateDelegate(delegateType, mi));
            System.Delegate getName = CreateDelegate(mi, null);

            object name = getName.DynamicInvoke(null);
            this.m_name = System.Convert.ToString(name);
        }

    } // End Class DynamicDllImportAttribute 


    public class DynamicDllImportTest 
    {

        private static string GetFreetypeName()
        {
            if (System.Environment.OSVersion.Platform == System.PlatformID.Unix)
                return "libfreetype.so.6";

            return "freetype6.dll";
        }


        // [DynamicDllImportAttribute("freetype6")]
        [DynamicDllImportAttribute(typeof(DynamicDllImportTest), "GetFreetypeName")]
        public static string bar()
        {
            return "foobar";
        }


        // NetStandardReporting.DynamicDllImportTest.Test();
        public static void Test()
        {
            System.Reflection.MethodInfo mi = typeof(DynamicDllImportTest).GetMethod("bar",
                  System.Reflection.BindingFlags.Static
                | System.Reflection.BindingFlags.Public
                | System.Reflection.BindingFlags.NonPublic);

            object[] attrs = mi.GetCustomAttributes(true);
            foreach (object attr in attrs)
            {
                DynamicDllImportAttribute importAttr = attr as DynamicDllImportAttribute;
                if (importAttr != null)
                {
                    System.Console.WriteLine(importAttr.Name);
                }
            } // Next attr 

        } // End Sub Test 


    } // End Class 


} // End Namespace 
1
Stefan Steiger