【问题标题】:Is it possible to have a delegate as attribute parameter?是否可以将委托作为属性参数?
【发布时间】:2011-12-04 01:24:36
【问题描述】:

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

像这样:

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

如果不可能,有什么明智的选择?

【问题讨论】:

    标签: c# delegates custom-attributes


    【解决方案1】:

    不,您不能将委托作为属性构造函数参数。查看可用类型: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
    {
    }
    

    【讨论】:

    • 其实这是一个很好的解决方法。我为此构建了一个特定的接口,但是委托要容易得多。谢谢!
    • 这是一种解决方法,但不是一个好方法。就像@nemesv 所说的那样 - hacky 并且容易出错,因为如果您使用重构菜单将方法 GetConnection 的名称更改为其他名称,则字符串“GetConnection”将不会自动更改。
    • 那么在c#中是否有可能手动限制构造函数的参数类型?我没有听到任何关于它的消息,但正如我们在Attribute 类型中看到的那样,这是可能的。怎么样?
    • 此限制直接内置于语言规范中。所以你不能对你的自定义类型做同样的事情,它只适用于属性。
    • 值得注意的是,现在使用 C# 6 您可以改为: [Workspace(typeof(TestDelegate), nameof(TestDelegate.GetConnection))] 这似乎可以解决有关它的问题由于重构容易出错。
    【解决方案2】:

    其他可能的解决方法是使用与您的委托定义匹配的抽象方法创建抽象基 Attribute 类型,然后在具体的 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
    {
    }
    

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

        【解决方案4】:

        亡灵术。
        增强了接受的答案以使用动态委托类型:

        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 
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-10-29
          • 1970-01-01
          • 2010-10-08
          • 2015-09-20
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多