C#如何在不创建实例的情况下获取属性值?

39

是否可以在不创建实例的情况下获取值?

我有这个类:

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

    public MyClass()
    {
    }
}

现在我需要获取值为"David"的数据,但不想创建MyClass实例。


将 Name 属性设为静态的。 - HatSoft
18
我很想知道这里所谓的使用场景是什么,让原帖作者认为他们应该这样做。 - Tim
@Tim:可能是因为原帖作者试图使用非实例属性,但不知道如何操作。在类上使用静态属性有很多用例。 - Mike Bailey
当然。也许我应该说,“假设OP想要的不仅仅是静态属性…” - Tim
8个回答

98
真正的答案: 不行。这是一个实例属性,你只能在实例上调用它。你应该创建一个实例或者像其他答案中所示,将属性设置为静态的。
更多关于静态和实例成员的信息,请参阅 MSDN挖苦但仍然正确的答案
"有没有可能在不创建实例的情况下获取值?"
是的,但只能使用一些非常可怕的代码,通过创建一些传递null作为this(在属性中不使用)的IL代码,使用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());
    }
}

请不要这样做。永远不要这样做。这太可怕了。它应该被践踏、切碎成小片、点燃,然后再切碎。但是,这很有趣,不是吗?;)

这段代码之所以能够工作,是因为它使用了call而不是callvirt。通常,即使它并没有调用虚拟成员函数,C#编译器也会使用callvirt调用,因为这可以在IL流中“免费”进行空引用检查。与此不同的是,像这样的非虚拟调用就不会先检查是否为空,而是直接调用成员函数。如果你在属性调用中检查了this,你会发现它是空的。

编辑:如Chris Sinclair所指出的那样,您可以使用一个开放式委托实例来更简单地实现它:

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

(但请不要这样做!)


7
@JonSkeet你能使用这里指出的技巧来避免生成中间语言吗?https://dev59.com/Z0fSa4cB1Zd3GeqPAt6c 编辑:嗯,也许不行。那是针对方法而不是属性的。 - Chris Sinclair
@ChrisSinclair:可能可以。我会试试看。 - Jon Skeet
4
虽然我不完全理解,但我仍然感到印象深刻。^^ - radbyx
2
@ChrisSinclair:不错 - 我已经将代码编辑到答案中了。 - Jon Skeet
@vikiiii: 我不知道,恐怕不行。 - Jon Skeet
显示剩余11条评论

68

你可以将该属性设置为 静态

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

使用方法:

MyClass.Name;

19
我认为这个答案比得分更高的徒劳无功的练习对问问题者更加有帮助,所以我点了一个赞。 - Michiel van Oosterhout
10
你可以认为我的回答开头给出了两个选项——要么使属性静态,要么创建一个实例。但这个回答只给出了其中一个选项 :) 然而,其他的回答已经涵盖了如何使属性静态的示例。我认为提供给不只是问题提出者的人有用的解答也很有意义。 - Jon Skeet
啊,这是最被接受的答案。如果我是楼主,那么我一定会这样做。 - avirk

5

你的要求似乎有些奇怪,但我认为你在寻找某种元数据。您可以使用属性来实现此目的:

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

4

正如许多人指出的那样,您可以将属性设置为静态

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

请注意,这意味着您的 MyClass 实例将不再具有自己的 Name 属性,因为静态成员属于类,而不是它的单个对象实例。
编辑: 在一条注释中,您提到希望在子类中覆盖 Name 属性。同时,您希望能够在类级别上访问它(在不创建类的实例的情况下访问它)。
对于静态属性,您只需在每个类中创建一个新的 Name 属性。由于它们是静态的,所以几乎总是(除了反射之外)使用特定的类来访问它们,因此您将指定要获取哪个版本的 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或static readonly变量。请参考此处此处了解更多信息。
public const string Name = "David";
public static readonly string Name = "David";

两者的使用方式相同:

string name = MyClass.Name;
< p > const 的主要好处(和缺点)是编译代码时所有对它的引用实际上都被替换为它的值。这意味着速度会更快,但如果您改变了它的值,则需要重新编译所有引用它的代码。


感谢大家的回复。但我需要覆盖 Name 属性:public class Dad { public virtual string Name { get { return "George"; } } public Dad() { } } public class Son : Dad { public override string Name { get{ return "Frank"; } } public Son() : base() { } }它可以被静态覆盖吗? - user1475694
静态属性不能是虚拟的,但是你不需要它们是虚拟的,因为你在类级别访问它们,这意味着你总是指定要访问哪个类的Name属性。 - Jon Senchyna
1
+1 表示 MyClass 的实例将不再具有 Name 属性。附注:父类继承自子类?并非每个儿子都是爸爸,但每个爸爸都是儿子。:P - lesderid
非常正确。我同意这个例子中的继承链很奇怪,但我只是使用上面评论中给出的方案。 - Jon Senchyna

2
每当你编写C#代码时,请始终检查你的方法和属性getter/setter代码是否与类的其他实例成员有任何关联。如果没有,请务必应用static关键字。在这里,它显然可以轻松解决你的问题。
我真正发表对这个问题的看法是,在一些答案中存在着语言偏见。在C#中,不能在空对象上调用实例方法是一个特定的C#语言规则。毫无疑问,这是一个非常明智的规则,它确实有助于解决NullReferenceExceptions问题,因为它们会在调用站点引发,而不是在方法内部某个地方引发,这样很难诊断this引用是否为空。
但这并不是CLR的要求,也不是运行在CLR上的每种语言的要求。事实上,即使是C#也不能始终强制执行它,你可以很容易地通过扩展方法绕过它。
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

即使使用不具有相同规则的语言的属性也可以正常工作。例如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;
}

1
创建一个静态属性:
public class MyClass
{
    public static string Name { get { return "David"; } }

    public MyClass()
    {
    }
}

像这样获取:

string name1 = MyClass.Name;

0
创建一个静态类或静态属性,就不需要显式实例化它。

0

这是不可能的。由于Name是一个实例属性,只有当你有一个实例时才能获取它的值。

此外,请注意你所谈论的不是一个参数,而是一个属性


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接