Можно ли иметь делегата в качестве параметра атрибута? - PullRequest
43 голосов
/ 09 октября 2011

Возможно ли иметь делегата в качестве параметра атрибута?

Примерно так:

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
{
}

И если это невозможно, каковы разумные альтернативы?

Ответы [ 4 ]

37 голосов
/ 09 октября 2011

Нет, вы не можете иметь делегата в качестве параметра конструктора атрибута. Просмотреть доступные типы: Типы параметров атрибута
В качестве обходного пути (хотя он хакерский и подвержен ошибкам) ​​вы можете создать делегат с отражением :

[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
{
}
13 голосов
/ 25 марта 2015

Другой возможный обходной путь - создание абстрактного базового типа атрибута с абстрактным методом, соответствующим определению вашего делегата, а затем реализация метода в конкретном классе атрибутов.

Имеет следующие преимущества:

  • Аннотация более краткая и чистая (как 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
{
}
1 голос
/ 05 сентября 2018

Некромантия.
Дополнен принятый ответ для использования динамического типа делегата:

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 голос
/ 20 апреля 2017

Я решил это, используя перечисление и массив отображений делегатов.Хотя мне действительно нравится идея использования наследования, в моем сценарии мне потребовалось бы написать несколько дочерних классов для выполнения относительно простых вещей.Это также должно быть рефакторингом.Единственным недостатком является то, что вы должны убедиться, что индекс делегата в массиве соответствует значению enum.

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);
}
...