Domanda

  

Eventuali duplicati:
   Trovare il Nome variabile passato a una funzione in C #

In C #, c'è un modo (terser il migliore) per risolvere il nome di un parametro in fase di esecuzione?

Ad esempio, nel metodo seguito, se è stato rinominato il parametro del metodo, si potrebbe anche ricordarsi di aggiornare la stringa letterale passata a ArgumentNullException.

    public void Woof(object resource)
    {
        if (resource == null)
        {
            throw new ArgumentNullException("resource");
        }

        // ..
    }
È stato utile?

Soluzione

Un modo:

static void Main(string[] args)
{
  Console.WriteLine("Name is '{0}'", GetName(new {args}));
  Console.ReadLine();
}

Questo codice richiede anche una funzione portante:

static string GetName<T>(T item) where T : class
{
  var properties = typeof(T).GetProperties();
  Enforce.That(properties.Length == 1);
  return properties[0].Name;
}

In pratica il codice funziona definendo un nuovo tipo anonimo con una sola offerta si compone di parametro che è il nome che si desidera. GetName () poi utilizza la reflection per estrarre il nome di quella proprietà.

Ci sono ulteriori dettagli qui: http://abdullin.com/journal/2008/12/13/how-to-find-out-variable-or-parameter-name-in-c.html

Altri suggerimenti

Risposta breve: No, non c'è. (È che terso sufficiente;?)

(EDIT:.. La risposta di Justin probabilmente conta Si lascia l'amaro in bocca, ma si realizza l'obiettivo di "non c'è bisogno di mettere il nome del parametro in una stringa" non credo che avrei davvero contare AOP anche se, come che è davvero il passaggio ad un approccio completamente diverso, piuttosto che rispondere alla domanda originale di ottenere un nome di parametro all'interno di un metodo.)

Più rispondere:. C'è un modo per scoprire tutti i parametri di un metodo, ma non credo che sia utile in questo caso

Ecco un esempio che mostra i nomi dei parametri da un paio di metodi:

using System;
using System.Reflection;

class Test
{
    static void Main()
    {
        Foo(null);
        Bar(null);
    }

    static void Foo(object resource)
    {
        PrintParameters(MethodBase.GetCurrentMethod());
    }

    static void Bar(object other)
    {
        PrintParameters(MethodBase.GetCurrentMethod());
    }

    static void PrintParameters(MethodBase method)
    {
        Console.WriteLine("{0}:", method.Name);
        foreach (ParameterInfo parameter in method.GetParameters())
        {
            Console.WriteLine(" {0} {1}",
                              parameter.ParameterType,
                              parameter.Name);
        }
    }
}

Quindi, che lo fa, ma se si dispone di più parametri e si voleva lanciare un'eccezione appropriata, come si sa (in modo sicuro), che da usare? Idealmente si desidera qualcosa di simile:

public void Woof(object resource)
{
    if (resource == null)
    {
        throw new ArgumentNullException(infoof(resource));
    }

    // ..
}

dove il mitico infoof operatore dovrebbe restituire un ParameterInfo. Purtroppo questo non esiste.

mi sono occupato di questo stesso problema. Ci sono un paio di modi per ottenere nome del parametro, ma il più performante è quello di immergere giù nella IL. È possibile vedere un esempio di mia implementazione sul mio blog post su questo tema Prendendo il dolore di convalida dei parametri .

L'unico avvertimento a questo approccio è è necessario passare il nome del parametro in qualità di delegato, ma è piccolo prezzo da pagare per il codice più pulito:

public void SomeMethod(string value)
{
    Validate.Argument(() => value).IsNotNull().IsNotEmpty();
}

Il che è un po 'più pulita e più chiara:

public void SomeMethod(string value)
{
    if (value == null)
    {
        throw new ArgumentNullException("value");
    }
    if (value == string.Empty)
    {
        throw new ArgumentException("Value cannot be an empty string.", "value");
    }
}

L'approccio metodo statico mi ha permesso di catena un numero di metodi insieme in un'interfaccia fluida. Inizialmente un oggetto Argument viene restituito, che consente solo un test di nulla di base che restituisce un oggetto ReferenceArgument che possono poi avere un'ulteriore convalida. Se l'oggetto da misurare è un tipo di valore, allora sono disponibili diversi test.

L'API consente una serie di test comuni ma sarebbe difficile catturare tutte le prove possibili in modo da fornire flessibilità un metodo generico permette un'espressione o funzionare da fornire e nel caso del primo dell'espressione può effettivamente essere utilizzato come il messaggio di errore.

Il mio esempio copre solo alcune delle nozioni di base, ma si può facilmente espandere l'interfaccia per verificare la presenza di catene e gettare ArgumentOutOfRangeExceptions o oggetti di test ereditare da una classe di base specifica o implementare un'interfaccia. Ci sono alcune implementazioni simili, ma non ho ancora visto nessuna che ottiene il nome del parametro.

È possibile ottenere queste informazioni utilizzando AOP. È possibile definire un'intercettazione che viene richiamata prima dell'esecuzione metodo e generare l'eccezione c'è. Questo si occupa anche il problema che il controllo nullo è una preoccupazione trasversale.

PostSharp è un buon semplice implementazione di AOP.

Ecco quello che il codice sarebbe simile (non ho ancora testato, ma dovrebbe arrivare molto vicino)

[AttributeUsage(AttributeTargets.Parameter)]
public class CanBeNullAttribute : Attribute
{
    private readonly bool canBeNull;

    public CanBeNullAttribute()
        : this(true)
    {
    }

    public CanBeNullAttribute(bool canBeNull)
    {
        this.canBeNull = canBeNull;
    }

    public bool AllowNull
    {
        get { return canBeNull; }
    }
}

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class EnforceNullConstraintAttribute : OnMethodInvocationAspect
{
    public override void OnInvocation(MethodInvocationEventArgs eventArgs)
    {
        object[] arguments = eventArgs.GetArgumentArray();
        ParameterInfo[] parameters = eventArgs.Delegate.Method.GetParameters();

        for (int i = 0; i < arguments.Length; i++)
        {
            if (arguments[i] != null) continue;

            foreach (CanBeNullAttribute attribute in parameters[i].GetCustomAttributes(typeof(CanBeNullAttribute), true))
            {
                if (!attribute.AllowNull) throw new ArgumentNullException(parameters[i].Name);
            }
        }

        base.OnInvocation(eventArgs);
    }
}

Ora, è possibile modificare il metodo:

[EnforceNullConstraint]
public void Woof([CanBeNull(false)] object resource)
{
    // no need to check for null, PostSharp will weave it at compile time

    // execute logic assured that "resource" is not null
}

Si potrebbe desiderare:

1)

public static void ThrowIfNull<T>(Expression<Func<T>> expr)
{
    if (expr == null || expr.Compile()() != null) //the compile part is slow
        return;

    throw new ArgumentNullException(((MemberExpression)expr.Body).Member.Name);
}

o

2)

public static void ThrowIfNull<T>(Expression<Func<T>> expr)
{
    if (expr == null)
        return;

    var param = (MemberExpression)expr.Body;
    if (((FieldInfo)param.Member).GetValue(((ConstantExpression)param.Expression).Value) == null)
        throw new ArgumentNullException(param.Member.Name);
}

E lo chiamano:

Class.ThrowIfNull(() => resource);

Ma non è questo ciò che si vorrebbe probabilmente. E 'anche molto più lento 1) è ABT 1000 volte più lento di 2). Può essere:

3)

public static void ThrowIfNull<T>(this T item) where T : class
{
    if (item == null)
        return;

    var param = typeof(T).GetProperties()[0];
    if (param.GetValue(item, null) == null)
        throw new ArgumentNullException(param.Name);
}

E lo chiamano:

new { resource }.ThrowIfNull();

Cleaner, molto più veloce di sopra del 2! :)

È inoltre possibile estendere questi metodi per le proprietà degli oggetti. Per per esempio.,

new { myClass.MyProperty1 }.ThrowIfNull();

È possibile memorizzare nella cache i valori delle proprietà per migliorare ulteriormente le prestazioni, come i nomi di proprietà non cambiano durante il runtime. Vedere correlata domanda Trovare il nome della variabile passato a una funzione

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