问题
Is it possible to have a delegate as the parameter of an attribute?
Like this:
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
{
}
And if its not possible, what are the sensible alternatives?
回答1:
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
{
}
回答2:
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
{
}
回答3:
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);
}
回答4:
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
来源:https://stackoverflow.com/questions/7705386/is-it-possible-to-have-a-delegate-as-attribute-parameter