Подтвердить что ты не робот

С# Получить значение свойства без создания экземпляра?

Можно ли получить значение без создания экземпляра?

У меня есть этот класс:

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

    public MyClass()
    {
    }
}

Теперь мне нужно получить значение "Давид", не создавая экземпляр MyClass.

4b9b3361

Ответ 1

Реальный ответ: нет. Это свойство экземпляра, поэтому его можно вызвать только в экземпляре. Вы должны либо создать экземпляр, либо сделать свойство статическим, как показано в других ответах.

См. MSDN для получения дополнительной информации о различии между элементами static и экземплярами.

Язык в щеках, но все же правильный ответ:

Можно ли получить значение без создания экземпляра?

Да, но только через какой-то действительно ужасный код, который создает некоторый IL, проходящий в null как this (который вы не используете в своем свойстве), используя DynamicMethod. Пример кода:

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

Пожалуйста, не делайте этого. Ever. Это ужасно. Его следует растоптать, разрезать на маленькие кусочки, подожгли, а затем снова разрезать. Интересно, правда?;)

Это работает, потому что используется call вместо callvirt. Обычно компилятор С# будет использовать вызов callvirt, даже если он не вызывает виртуальный член, потому что он получает нулевую проверку ссылок "бесплатно" (насколько это касается потока IL). Не виртуальный вызов, подобный этому, сначала не проверяет значение nullity, он просто вызывает член. Если вы проверили this в вызове свойства, вы найдете его null.

EDIT: Как отметил Крис Синклер, вы можете сделать это проще, используя открытый экземпляр делегата:

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

(Но опять же, пожалуйста, не надо!)

Ответ 2

Вы можете сделать это свойство static

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

Использование:

MyClass.Name;

Ответ 3

Вы можете сделать свое свойство static, как указано многими другими.

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

Помните, что это означает, что ваши экземпляры MyClass больше не будут иметь собственное свойство Name, поскольку статические члены принадлежат классу, а не отдельные экземпляры объекта.

Edit: В примечании вы упомянули, что хотите переопределить свойство Name в подклассах. В то же время вы хотите иметь доступ к нему на уровне класса (обращайтесь к нему, не создавая экземпляр своего класса).

Для статических свойств вы просто создадите новое свойство Name в каждом классе. Поскольку они static, вы всегда (почти всегда, yay reflection) собираетесь обращаться к ним с помощью определенного класса, поэтому вы должны указать, какую версию Name вы хотите получить. Если вы хотите попробовать и взломать полиморфизм там и получить имя из какого-либо данного подкласса MyClass, вы можете сделать это с помощью отражения, но я бы не рекомендовал это делать.

Используя пример из вашего комментария:

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

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

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

Использование для обоих будет одинаковым:

string name = MyClass.Name;

Основное преимущество (и недостаток) const заключается в том, что все ссылки на него фактически заменяются его значением при компиляции кода. Это означает, что он будет немного быстрее, но если вы когда-либо измените его значение, вам нужно будет перекомпилировать ВСЕ код, который ссылается на него.

Ответ 4

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

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

Теперь этот код может получить имена с экземплярами и без них:

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

Ответ 5

Всякий раз, когда вы пишете код С#, всегда проверяйте, действительно ли ваш метод и код getter/setter кода вообще что-либо делают с другими членами класса. Если они этого не сделают, обязательно примените статическое ключевое слово. Конечно, здесь, это тривиально решает вашу проблему.

Причина, по которой я действительно публикую этот вопрос, заключается в том, что в некоторых ответах есть немного языковой предвзятости. Правило С#, которое вы не можете вызвать метод экземпляра для нулевого объекта, является конкретным правилом языка С#. Это, без сомнения, очень мудрый, он действительно помогает устранить NullReferenceExceptions, они поднимаются на сайт вызова, а не где-то внутри метода, где очень сложно диагностировать, что эта ссылка является нулевой.

Но это, конечно, не является обязательным условием для CLR, ни для каждого языка, который работает в среде CLR. На самом деле, даже С# не применяет его последовательно, вы можете легко обойти его в методе расширения:

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

И использование вашего свойства с языка, который не имеет того же правила, отлично работает. Как С++/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;
}

Ответ 6

Создать статическое свойство:

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

    public MyClass()
    {
    }
}

Получите его так:

string name1 = MyClass.Name;

Ответ 7

Создайте статический класс или статическое свойство, и вам не нужно явно его создавать.

Ответ 8

Это невозможно. Поскольку Name является свойством экземпляра, вы можете получить его значение только в том случае, если у вас есть экземпляр.

Также обратите внимание, что вы не говорите о параметре, а о свойстве.