Domanda

diciamo che ho

public delegate DataSet AutoCompleteDelegate(
      string filter, long rowOffset);

posso creare la seguente classe per imporre quella firma del metodo? (solo un'idea evocata):

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]

Scopo: ho già inserito gli attributi nei metodi del mio middletier. Ho metodi come questo:

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

quindi in fase di esecuzione, eseguirò l'iterazione di tutti i metodi del middletier e li metterò in raccolta (gli attributi aiutano molto qui), quindi li mapperò sulle funzioni delegate di Winform (facilitate dall'interfaccia, dal sistema basato su plugin) come caricate

Sto pensando se posso rendere gli attributi più auto-descrittivi, in modo che il compilatore possa rilevare incoerenze.

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

Sto pensando che se si mettono i metodi su ogni classe, sarebbe eccessivo creare un'istanza di un oggetto Remoting. Mettendoli in una classe separata, potrebbe essere più difficile facilitare il riutilizzo del codice, diciamo che Invoice_Save necessita di alcune informazioni su Receipt_Open. In realtà ho anche un rapporto qui (cristallo), che ha recuperato i dati da Remoting middletier DataSet, all'interno del metodo invocato, ottiene alcune informazioni su altri metodi e si fonde nel proprio DataSet, stanno tutti accadendo su Middletier, senza diversi roundtrip, tutti vengono eseguiti sul lato server (livello intermedio)

È stato utile?

Soluzione

Potresti implementare sia FollowAttribute che usi nel tuo esempio sia scrivere un'analisi statica (diciamo, FxCop ) regola che potrebbe verificare se qualsiasi metodo che è taggato con tale attributo ha la stessa firma del delegato indicato. Quindi dovrebbe essere possibile.

Altri suggerimenti

Altre risposte sono ovviamente valide ma nulla ti proteggerà dal dimenticare di applicare [Follow(AutoCompleteDelegate)] l'attributo sul tuo metodo.

Penso che faresti meglio a trasformare i metodi in classi che implementano un'interfaccia:

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

e quindi utilizza il modello del metodo di fabbrica per ottenere il tuo " completatori automatici < !> quot;

public static class AutoCompleteFactory
{
    public static IAutoComplete CreateFor(string purpose)
    {
        // build up and return an IAutoComplete implementation based on purpose.
    }
}

o

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

Una volta che hai potuto dare un'occhiata all'inversione del controllo e dell'iniezione di dipendenza per evitare una codifica rigida dell'elenco delle implementazioni complete automatiche nel tuo metodo di fabbrica.

Questa non è una funzione linguistica, ma ...

Questo è qualcosa per cui potresti fare la validazione: scrivere unit test che riflettono sulla classe e falliscono se la firma non corrisponde alla dichiarazione dell'attributo.

PostSharp ti offre anche alcune opzioni interessanti per fare ciò attorno alla compilazione. Non so come lo useresti esattamente, ma sospetto che potresti ...

Vorrei chiederti perché vorresti farlo. Se non si desidera che la classe venga modificata tramite ereditarietà, è possibile renderla una classe sigillata. Se sei preoccupato che qualcuno cambi classe in futuro, hai 1 caso su due. 1) Non capiscono cosa stanno facendo; nulla può impedire a un cattivo programmatore di fare cose cattive se ha il pieno dominio di modificare il testo del programma. 2) Stanno estendendo la funzionalità della classe in un modo che attualmente non capisci che fa male riutilizzare.

Gli attributi vengono memorizzati come meta-informazioni extra durante la compilazione: puoi interrogarli in fase di esecuzione ma durante la compilazione non vengono presi in considerazione.

Non puoi vincolare un metodo con un attributo su di esso. Puoi limitare la modalità di applicazione dell'attributo (ovvero solo sui metodi o se è possibile applicarne più di uno).

Suggerirei di usare FxCop per avvisare quando gli attributi non corrispondono - se stai attento al modo in cui gli eventi supportano il cast di tipi:

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

Sarebbe un delegato valido.

No.

Sorta di.

Non puoi ottenere questo comportamento al momento della compilazione. Con Attributi, puoi inserirlo in un semplice cablaggio di prova o forzare un errore immediato quando viene istanziata la classe contenente.

Considera i due attributi (rapidamente compromessi):

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

Lancia EnforceConforms (typeof (myFavoriteClass)) su una classe e Conforms (typeof (myFavoriteDelegate)) sui metodi pertinenti e quindi (questa è la parte hacky) typeof (myFavoriteClass) .GetCustomAttributes ( false). Potresti farlo in un inizializzatore statico per fallire & Quot; veramente veloce & Quot; o farlo in una classe di test (che cerca tutti i metodi nell'assembly con l'attributo EnforceConforms se vuoi essere sofisticato).

In generale, probabilmente non dovresti usarlo. Se la progettazione richiede di verificare le implementazioni dei delegati appropriate, è necessario riprogettarle, se possibile. Inoltre i frammenti di tempo di non compilazione lo rendono così non ti stai risparmiando molto nel tempo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top