Есть ли способ заставить метод следовать определенной сигнатуре метода?
Скажем у меня есть
public delegate DataSet AutoCompleteDelegate(
string filter, long rowOffset);
Могу ли я создать следующий класс для обеспечения подписи этого метода? (просто придуманная идея):
public class MiddleTier
{
[Follow(AutoCompleteDelegate)]
public DataSet Customer_AutoComplete(string filter, long rowOffset)
{
var c = Connect();
// some code here
}
[Follow(AutoCompleteDelegate)]
public DataSet Item_AutoComplete(string filter, long rowOffset)
{
var c = Connect();
// some code here
}
// this should give compilation error, doesn't follow method signature
[Follow(AutoCompleteDelegate)]
public DataSet BranchOffice_AutoComplete(string filter, string rowOffset)
{
var c = Connect();
// some code here
}
}
[РЕДАКТИРОВАТЬ]
Цель: я уже поместил атрибуты в методы моего посредника. У меня есть такие методы:
public abstract class MiddleTier : MarshalByRefObject
{
// Operation.Save is just an enum
[Task("Invoice", Operation.Save)]
public Invoice_Save(object pk, DataSet delta);
[Task("Receipt", Operation.Save)]
public Receipt_Save(object pk, DataSet delta);
// compiler cannot flag if someone deviates from team's standard
[Task("Receipt", Operation.Save)]
public Receipt_Save(object pk, object[] delta);
}
затем во время выполнения я буду перебирать все методы посредника и помещать их в коллекцию (здесь очень помогают атрибуты), а затем отображать их на функции делегата winform (облегченные интерфейсом, системой на основе плагинов) как загруженные
Я думаю, смогу ли я сделать атрибуты более самоописуемыми, чтобы компилятор мог улавливать несоответствия.
namespace Craft
{
// public delegate DataSet SaveDelegate(object pk, DataSet delta); // defined in TaskAttribute
public abstract class MiddleTier : MarshalByRefObject
{
[Task("Invoice", SaveDelegate)]
public abstract Invoice_Save(object pk, DataSet delta);
[Task("Receipt", SaveDelegate)]
// it's nice if the compiler can flag an error
public abstract Receipt_Save(object pk, object[] delta);
}
}
Я думаю, что если поместить методы в каждый класс, было бы излишним всегда создавать экземпляр объекта Remoting. И если поместить их в отдельный класс, может быть сложнее облегчить повторное использование кода, скажем, Invoice_Save нужна некоторая информация о Receipt_Open. На самом деле у меня даже есть отчет (кристалл), в котором извлекаются данные из Remoting промежуточного DataSet, внутри вызванного метода, он получает некоторую информацию о других методах и объединяется в свой собственный DataSet, все они происходят на промежуточном уровне, без нескольких циклических переходов, все делается на стороне сервера (средний уровень)
6 ответов
Вы могли бы реализовать как FollowAttribute
Вы используете в своем примере и пишете правило статического анализа (скажем, FxCop), которое может проверить, имеет ли какой-либо метод, помеченный этим атрибутом, ту же сигнатуру, что и у упомянутого делегата. Так что это должно быть возможно.
Другие ответы, очевидно, действительны, но ничто не защитит вас от забвения [Follow(AutoCompleteDelegate)]
атрибут вашего метода.
Я думаю, вам лучше превратить методы превращения в классы, реализующие интерфейс:
public interface IAutoComplete
{
DataSet Complete(string filter, long rowOffset);
}
public class CustomerAutoComplele : IAutoComplete
{
public DataSet Complete(string filter, long rowOffset)
{
var c = Connect();
// some code here
}
}
и затем используйте шаблон фабричного метода, чтобы получить ваши "автозаполнители":
public static class AutoCompleteFactory
{
public static IAutoComplete CreateFor(string purpose)
{
// build up and return an IAutoComplete implementation based on purpose.
}
}
или же
public static class AutoCompleteFactory
{
public static IAutoComplete CreateFor<T>()
{
// build up and return an IAutoComplete implementation based on T which
// could be Customer, Item, BranchOffice class.
}
}
Как только вы это сделаете, вы можете взглянуть на инверсию управления и внедрение зависимостей, чтобы избежать жесткого кодирования списка реализаций автозаполнения в вашем заводском методе.
Я бы спросил, почему вы хотите это сделать. Если вы не хотите, чтобы класс был изменен посредством наследования, вы можете сделать его запечатанным классом. Если вы беспокоитесь о том, что в будущем кто-то изменит класс, у вас есть 1 из двух случаев. 1) Они не понимают, что делают; ничто не может помешать плохому программисту совершать плохие поступки, если они имеют полное правление для редактирования текста программы. 2) Они расширяют функциональность класса таким образом, что вы не понимаете, что мешает повторному использованию.
Это не языковая функция, но...
Это то, что вы могли бы сделать для проверки: написать модульные тесты, которые отражают класс и потерпеть неудачу, если подпись не соответствует объявлению атрибута.
PostSharp также предоставляет вам несколько интересных возможностей для компиляции. Я не знаю, как именно вы бы это использовали, но я подозреваю, что вы могли бы...
Нет.
Вроде, как бы, что-то вроде.
Вы не можете получить это поведение во время компиляции. Вы можете с помощью атрибутов перенести это в простой тестовый комплект или вызвать немедленный сбой при создании экземпляра содержащего класса.
Рассмотрим два (быстро взломанных) атрибута:
[AttributeUsage(AttributeTargets.Class)]
public class EnforceConforms : Attribute
{
public EnforceConforms(Type myClass)
: base()
{
MethodInfo[] info = myClass.GetMethods();
foreach (MethodInfo method in info)
{
object[] objs = method.GetCustomAttributes(false);
foreach (object o in objs)
{
Attribute t = (Attribute)o;
if (t.GetType() != typeof(ConformsAttribute)) continue;
MethodInfo mustConformTo = ((ConformsAttribute)t).ConformTo;
ParameterInfo[] info1 = mustConformTo.GetParameters();
ParameterInfo[] info2 = method.GetParameters();
bool doesNotCoform = false;
doesNotCoform |= (mustConformTo.ReturnType != method.ReturnType);
doesNotCoform |= (info1.Length != info2.Length);
if (!doesNotCoform)
{
for (int i = 0; i < info1.Length; i++)
{
ParameterInfo p1 = info1[i];
ParameterInfo p2 = info2[i];
if (!p1.ParameterType.Equals(p2.ParameterType))
{
doesNotCoform = true;
break;
}
}
}
if (doesNotCoform)
{
throw new Exception(myClass.Name + "." + method.Name + " does not conform to required delegate signature");
}
}
}
}
}
[AttributeUsage(AttributeTargets.Method)]
public class ConformsAttribute : Attribute
{
public MethodInfo ConformTo;
public ConformsAttribute(Type type)
: base()
{
if (type.BaseType != typeof(Delegate) && type.BaseType != typeof(System.MulticastDelegate)) throw new Exception("Can only accept delegates");
ConformTo = type.GetMethod("Invoke");
}
}
Бросьте EnforceConforms (typeof (myFavoriteClass)) на класс и Conforms (typeof (myFavoriteDelegate)) на соответствующие методы, а затем (это хакерская часть) typeof (myFavoriteClass).GetCustomAttributes (false). Вы можете сделать это в статическом инициализаторе, чтобы он "действительно быстро" завершился с ошибкой, или сделать это в тестовом классе (который ищет все методы в сборке с атрибутом EnforceConforms, если вы хотите получить фантазию).
Как правило, вы, вероятно, не должны использовать это. Если ваш дизайн требует от вас проверки правильных реализаций делегатов, вам следует по возможности изменить архитектуру. Кроме того, биты времени некомпиляции делают его таким образом, что вы не сильно экономите время.
Атрибуты хранятся в виде дополнительной метаинформации во время компиляции - вы можете запрашивать их во время выполнения, но во время компиляции они не учитываются.
Вы не можете ограничить метод атрибутом. Вы можете ограничить применение атрибута (т. Е. Только для методов или для применения более одного).
Я бы предложил использовать FxCop, чтобы предупредить, когда атрибуты не совпадают - если вы будете осторожны с тем, как события поддерживают приведение типов:
[Follow(AutoCompleteDelegate)]
public DataSet Customer_AutoComplete(string filter, int rowOffset)
Был бы действительным делегатом.