Is it possible to have a delegate as attribute parameter?

前端 未结 4 677
醉话见心
醉话见心 2020-11-28 07:41

Is it possible to have a delegate as the parameter of an attribute?

Like this:

public delegate IPropertySet ConnectionPropertiesDelegate();

public s         


        
相关标签:
4条回答
  • 2020-11-28 07:54

    I solved this by using an enum and a mapping array of delegates. Although I really like the idea of using inheritance, in my scenario that would require me to write several child classes to do relatively simple stuff. This should be refactorable as well. The only drawback is that you have to make sure to make the delegate's index in the array corresponds to the enum value.

    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);
    }
    
    0 讨论(0)
  • 2020-11-28 07:56

    No, you cannot have a delegate as an attribute constructor parameter. See available types: Attribute parameter types
    As a workaround (although it's hacky and error prone) you can create a delegate with reflection:

    [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
    {
    }
    
    0 讨论(0)
  • 2020-11-28 08:05

    Necromancing.
    Augmented on the accepted answer to use a dynamic delegate type:

    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 讨论(0)
  • 2020-11-28 08:06

    Other possible workaround is creating abstract base Attribute type with abstract method matching your delegate definition, and then implementing the method in concrete Attribute class.

    It has following benefits:

    • Annotation is more concise and clean (DSL like)
    • No reflection
    • Easy to reuse

    Example:

    [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
    {
    }
    
    0 讨论(0)
提交回复
热议问题