48 votes

Est-il possible d'avoir un délégué comme paramètre d'attribut ?

Est-il possible d'avoir un délégué comme paramètre d'un attribut ?

Comme ça :

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

Et si ce n'est pas possible, quelles sont les alternatives raisonnables ?

43voto

nemesv Points 71516

Non, vous ne pouvez pas avoir un délégué comme paramètre de constructeur d'attribut. Voir les types disponibles : Types de paramètres d'attributs
En guise de solution de rechange (bien que cela soit compliqué et source d'erreurs), vous pouvez créer un fichier de type délégué avec réflexion :

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

15voto

dadhi Points 11

Une autre solution possible consiste à créer un type d'attribut de base abstrait avec une méthode abstraite correspondant à votre définition de délégué, puis à mettre en œuvre la méthode dans la classe d'attribut concrète.

Il présente les avantages suivants :

  • L'annotation est plus concise et plus propre (style DSL).
  • Pas de réflexion
  • Facile à réutiliser

Exemple :

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

1voto

Jeff Sall Points 11

J'ai résolu ce problème en utilisant un enum et un tableau de correspondance de délégués. Bien que j'aime beaucoup l'idée d'utiliser l'héritage, dans mon scénario, cela m'obligerait à écrire plusieurs classes enfant pour faire des choses relativement simples. Cela devrait également pouvoir être refactorisé. Le seul inconvénient est que vous devez vous assurer que l'index du délégué dans le tableau correspond à la valeur de l'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);
}

1voto

Quandary Points 12867

Nécromancie.
Augmenté sur la réponse acceptée d'utiliser un type de délégué dynamique :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X