Есть ли способ заставить метод следовать определенной сигнатуре метода? - PullRequest
5 голосов
/ 29 июня 2009

скажем, у меня есть

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
    }         

}

[EDIT]

Цель: я уже поместил атрибуты в методы моего посредника. У меня есть такие методы:

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 ]

11 голосов
/ 29 июня 2009

Другие ответы, очевидно, верны, но ничто не защитит вас от забвения применения атрибута [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.
    }
}

Как только вы это сделаете, вы сможете взглянуть на инверсию управления и внедрение зависимостей, чтобы избежать жесткого кодирования списка реализаций автозаполнения в вашем заводском методе.

3 голосов
/ 29 июня 2009

Вы можете реализовать как FollowAttribute, который вы используете в своем примере, так и написать правило статического анализа (скажем, FxCop) , которое может проверить, имеет ли какой-либо метод, помеченный этим атрибутом, такую ​​же сигнатуру, как упомянутый делегат. Так что это должно быть возможно.

1 голос
/ 29 июня 2009

Я бы спросил, почему вы хотите это сделать. Если вы не хотите, чтобы класс был изменен посредством наследования, вы можете сделать его запечатанным классом. Если вы беспокоитесь о том, что в будущем кто-то изменит класс, у вас есть 1 из двух случаев. 1) они не понимают, что делают; ничто не может помешать плохому программисту совершать плохие поступки, если они имеют полное правление для редактирования текста программы. 2) Они расширяют функциональность класса таким образом, что вы не понимаете, что мешает повторному использованию.

1 голос
/ 29 июня 2009

Это не языковая функция, но ...

Это то, что вы могли бы сделать для проверки: написать модульные тесты, отражающие класс, и потерпеть неудачу, если подпись не соответствует объявлению атрибута.

PostSharp также дает вам несколько интересных опций для компиляции.Я не знаю, как именно вы бы это использовали, но я подозреваю, что вы могли бы ...

0 голосов
/ 29 июня 2009

номер

Сортировка.

Вы не можете получить это поведение во время компиляции. Вы можете с помощью атрибутов перенести это в простой тестовый комплект или вызвать немедленный сбой при создании экземпляра содержащего класса.

Рассмотрим два (быстро взломанных) атрибута:

[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, если вы хотите получить фантазию).

Как правило, вы, вероятно, не должны использовать это. Если ваш дизайн требует от вас проверки правильных реализаций делегатов, вам следует по возможности изменить архитектуру. Кроме того, биты времени некомпиляции делают его таким образом, что вы не особо экономите время.

0 голосов
/ 29 июня 2009

Атрибуты хранятся в виде дополнительной метаинформации во время компиляции - вы можете запрашивать их во время выполнения, но во время компиляции они не учитываются.

Нельзя ограничить метод атрибутом. Вы можете ограничить применение атрибута (т. Е. Только для методов или для применения более одного).

Я бы предложил использовать FxCop, чтобы предупредить, когда атрибуты не совпадают - если вы будете осторожны с тем, как события поддерживают приведение типов:

[Follow(AutoCompleteDelegate)]
public DataSet Customer_AutoComplete(string filter, int rowOffset)

Будет действительным делегатом.

...