34 votes

C# Obtenir la valeur de la propriété sans créer d'instance?

Est-il possible d'obtenir la valeur sans la création d'une instance ?

J'ai cette classe:

public class MyClass
{
    public string Name{ get{ return "David"; } }

    public MyClass()
    {
    }
}

Maintenant, j'ai besoin de récupérer la valeur de "David", sans créer d'instance de Maclasse.

93voto

Jon Skeet Points 692016

La vraie réponse: non. C'est une instance de la propriété, de sorte que vous ne pouvez l'appeler que sur une instance. Vous devez créer une instance, ou de faire de la propriété statique comme dans d'autres réponses.

Voir MSDN pour plus d'informations à propos de la différence entre statique et les membres de l'instance.

La langue-dans-joue, mais encore la réponse correcte:

Est-il possible d'obtenir la valeur sans la création d'une instance ?

Oui, mais seulement par le biais de certains de vraiment horrible de code qui crée IL en passant en null comme this (que vous n'utilisez pas dans votre propriété), à l'aide d'un DynamicMethod. Exemple de code:

// Jon Skeet explicitly disclaims any association with this horrible code.
// THIS CODE IS FOR FUN ONLY. USING IT WILL INCUR WAILING AND GNASHING OF TEETH.
using System;
using System.Reflection.Emit;

public class MyClass
{
    public string Name { get{ return "David"; } }
}


class Test    
{
    static void Main()
    {
        var method = typeof(MyClass).GetProperty("Name").GetGetMethod();
        var dynamicMethod = new DynamicMethod("Ugly", typeof(string), 
                                              Type.EmptyTypes);
        var generator = dynamicMethod.GetILGenerator();
        generator.Emit(OpCodes.Ldnull);
        generator.Emit(OpCodes.Call, method);
        generator.Emit(OpCodes.Ret);
        var ugly = (Func<string>) dynamicMethod.CreateDelegate(
                       typeof(Func<string>));
        Console.WriteLine(ugly());
    }
}

Merci de ne pas le faire. Jamais. C'est horrible. Il devrait être foulé aux pieds, coupé en petits morceaux, mettre le feu, puis couper à nouveau. Plaisir, n'est-ce pas? ;)

Cela fonctionne parce que c'est à l'aide de call au lieu de callvirt. Normalement le compilateur C# serait d'utiliser un callvirt appel , même si elle n'est pas d'appeler un membre virtuel parce qu'obtient la valeur null de la vérification des références "gratuitement" (dans la mesure où IL stream est concerné). Un non-appel virtuel comme ce n'est pas de vérifier la nullité d'abord, il a juste invoque le membre. Si vous avez coché this dans l'appel de propriété, que vous pouvez trouver, c'est la valeur null.

EDIT: Comme l'a noté Chris Sinclair, vous pouvez le faire plus simplement à l'aide d'une ouverture d'instance de délégué:

var method = typeof(MyClass).GetProperty("Name").GetGetMethod();
var openDelegate = (Func<MyClass, string>) Delegate.CreateDelegate
    (typeof(Func<MyClass, string>), method);
Console.WriteLine(openDelegate(null));

(Mais encore une fois, merci de ne pas!)

62voto

alexm Points 3064

Vous pouvez faire cette propriété statique

public static string Name{ get{ return "David"; } } 

Utilisation:

MyClass.Name;

4voto

Jon Senchyna Points 4584

Vous pouvez faire de votre propriété statique, comme l'a souligné par beaucoup d'autres.

public static string Name{ get{ return "David"; } }

Être conscient que cela signifie que vos instances de MyClass n'ont plus leur Nom propre propriété, depuis que des membres statiques appartiennent à la classe, et non de l'objet individuel instances de celui-ci.

Edit: Dans une note, vous avez mentionné que vous voulez remplacer le Nom de la propriété dans les sous-classes. Dans le même temps, vous voulez être en mesure d'accéder au niveau de la classe (accès sans créer une instance de votre classe).

Pour les propriétés statiques, il vous suffit de créer un nouveau Name propriété dans chaque classe. Depuis qu'ils sont static, vous êtes toujours (presque toujours, yay réflexion) pour accéder à l'aide d'une classe spécifique, de sorte que vous seriez en spécifiant la version d' Name que vous souhaitez obtenir. Si vous voulez essayer et hack polymorphisme là-bas et obtenir le nom à partir de n'importe quelle sous-classe de Maclasse, vous pouvez le faire en utilisant la réflexion, mais je ne recommanderais pas le faire.

En utilisant l'exemple de votre commentaire:

public class Dad 
{ 
    public static string Name { get { return "George"; }
}

public class Son : Dad
{
    public static string Name { get{ return "Frank"; }
}

public static void Test()
{
    Console.WriteLine(Dad.Name); // prints "George"
    Console.WriteLine(Son.Name); // prints "Frank"
    Dad actuallyASon = new Son();
    PropertyInfo nameProp = actuallyASon.GetType().GetProperty("Name");
    Console.WriteLine(nameProp.GetValue(actuallyASon, null)); // prints "Frank"
}

Comme une note de côté, puisque vous déclarez une propriété qui n'a qu'un getter et il est de retourner une valeur constante, je recommande éventuellement à l'aide d'un const ou statique en lecture seule variable à la place.

public const string Name = "David";
public static readonly string Name = "David";

Utilisation pour les deux serait la même:

string name = MyClass.Name;

Le principal avantage (et l'inconvénient) d' const , c'est que toutes les références sont en fait remplacé par sa valeur lorsque le code est compilé. Ce qui signifie qu'il sera un peu plus vite, mais si jamais vous changez sa valeur, vous aurez besoin de recompiler TOUT le code qui fait référence à elle.

3voto

Jordão Points 29221

Les exigences que vous ne sembler étrange, mais je pense que vous êtes à la recherche d'une sorte de méta-données. Vous pouvez utiliser un attribut de réaliser ceci:

public class NameAttribute : Attribute {
  public string Name { get; private set; }
  public NameAttribute(string name) {
    Name = name;
  }
}

[Name("George")]
public class Dad { 
  public string Name { 
    get { 
      return NameGetter.For(this.GetType()); 
    }
  }
}

[Name("Frank")]
public class Son : Dad {
}

public static class NameGetter {
  public static string For<T>() {
    return For(typeof(T));
  }
  public static string For(Type type) {
    // add error checking ...
    return ((NameAttribute)type.GetCustomAttributes(typeof(NameAttribute), false)[0]).Name;
  }
}

Maintenant, ce code peut obtenir des noms avec et sans instances:

Console.WriteLine(new Dad().Name);
Console.WriteLine(new Son().Name);
Console.WriteLine(NameGetter.For<Dad>());
Console.WriteLine(NameGetter.For<Son>());

2voto

Hans Passant Points 475940

Chaque fois que vous écrivez le code C#, vérifiez toujours si votre méthode et propriété de lecture/définition code ne fait rien du tout avec les autres membres de l'instance de la classe. S'ils ne le font pas, assurez-vous d'appliquer la statique mot-clé. Certainement le cas ici, il trivialement de résoudre votre problème.

La raison pour laquelle je vraiment écrire à cette question est qu'il y a un peu de biais lié à la langue à l'œuvre dans certaines des réponses. Le C# règle que vous ne pouvez pas appeler une méthode d'une instance sur un objet nul est spécifique du langage C# règle. Il est sans doute très sage, il aide vraiment à résoudre exception nullreferenceexceptions, ils sont élevés sur le site d'appel au lieu de quelque part à l'intérieur d'une méthode où ça devient très difficile à diagnostiquer que la cette référence est nulle.

Mais ce n'est certainement pas une exigence pour le CLR, ni de toute langue qui s'exécutent sur le CLR. En fait, même C# ne pas appliquer de manière cohérente, vous pouvez facilement le contourner dans une méthode d'extension:

public static class Extensions {
    public static bool IsNullOrEmpty(this string obj) {
        return obj != null && obj.Length > 0;
    }
}
...
        string s = null;
        bool empty = s.IsNullOrEmpty();    // Fine

Et l'utilisation de votre propriété à partir d'une langue qui n'a pas la même règle fonctionne très bien. Comme C++/CLI:

#include "stdafx.h"

using namespace System;
using namespace ClassLibrary1;    // Add reference

int main(array<System::String ^> ^args)
{
    MyClass^ obj = nullptr;
    String^ name = obj->Name;     // Fine
    Console::WriteLine(name);
    return 0;
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X