Возможно ли иметь делегат в качестве параметра атрибута?
Возможно ли иметь делегат в качестве параметра атрибута?
Вот так:
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
{
}
И если это невозможно, каковы разумные альтернативы?
Ответы
Ответ 1
Нет, у вас не может быть делегата в качестве параметра конструктора атрибутов. См. Доступные типы: Типы параметров атрибутов
В качестве обходного пути (хотя он взломан и подвержен ошибкам) вы можете создать делегат с отражением:
[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(delegateType, delegateType.GetMethod(delegateName));
}
}
[Workspace(typeof(TestDelegate), "GetConnection")]
public class Test
{
}
Ответ 2
Другим возможным обходным путем является создание абстрактного базового типа атрибута с абстрактным методом, соответствующим вашему определению делегата, а затем реализация метода в конкретном классе атрибута.
Он имеет следующие преимущества:
- Аннотации более сжатые и чистые (например, 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);
}