web-dev-qa-db-fra.com

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?

43
George Silva

Non, vous ne pouvez pas avoir un délégué en tant que paramètre constructeur d'attribut. Voir les types disponibles: Types de paramètres d'attributs
En guise de solution de contournement (bien que cela soit sujet aux erreurs et aux hackers), vous pouvez créer un 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
{
}
36
nemesv

Une autre solution possible consiste à créer un type d'attribut de base abstraite avec une méthode abstraite correspondant à votre définition de délégué, puis à l'implémenter dans une classe d'attributs concrète. 

Il présente les avantages suivants:

  • L'annotation est plus concise et propre (comme 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
{
}
13
dadhi

J'ai résolu ce problème en utilisant une énumération et un tableau de délégués mappé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 effectuer des tâches relativement simples. Cela devrait être refactorable aussi. Le seul inconvénient est que vous devez vous assurer que l'index du délégué dans le tableau correspond à la valeur 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);
}
1
Jeff Sall

Nécromancie.
Augmenté sur la réponse acceptée pour 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 
0
Stefan Steiger