是否可以将委托作为属性参数?

时间:2011-10-09 17:57:08

标签: c# delegates custom-attributes

是否可以将委托作为属性的参数?

像这样:

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 个答案:

答案 0 :(得分:37)

不,您不能将委托作为属性构造函数参数。查看可用类型:Attribute parameter types
作为一种解决方法(虽然它很容易出错并且容易出错)你可以创建一个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
{
}

答案 1 :(得分:13)

其他可能的解决方法是使用与委托定义匹配的抽象方法创建抽象基类属性类型,然后在具体的Attribute类中实现该方法。

它有以下好处:

  • 注释更简洁干净(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
{
}

答案 2 :(得分:1)

我通过使用枚举和委托的映射数组解决了这个问题。虽然我非常喜欢使用继承的想法,但在我的场景中,我需要编写几个子类来做相对简单的事情。这也应该是可重构的。唯一的缺点是你必须确保使数组中的委托索引对应于枚举值。

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

答案 3 :(得分:1)

创新。
扩大了接受的答案,以使用动态委托类型:

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