37

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

我有这堂课:

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

    public MyClass()
    {
    }
}

现在我需要获取值“David”,而不创建 MyClass 的实例。

4

8 回答 8

96

真实答案:没有。它是一个实例属性,因此您只能在实例上调用它。您应该创建一个实例,或者将属性设为静态,如其他答案所示。

有关静态成员和实例成员之间区别的更多信息,请参阅MSDN 。

面面相觑,但仍然正确的答案

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

是的,但只能通过一些非常可怕的代码创建一些 IL 传入nullthis您不会在您的财产中使用),使用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));

(但同样,请不要!)

于 2012-06-22T19:29:26.780 回答
67

您可以将该属性设为静态

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

用法:

MyClass.Name;
于 2012-06-22T19:12:41.863 回答
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>());
于 2012-06-22T23:20:38.493 回答
4

正如许多其他人所指出的那样,您可以将您的财产设为static 。

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

请注意,这意味着您的 MyClass 实例将不再拥有自己的 Name 属性,因为静态成员属于该类,而不是它的单个对象实例。

编辑: 在注释中,您提到要覆盖子类中的 Name 属性。同时,您希望能够在类级别访问它(访问它而不创建类的实例)。

对于静态属性,您只需Name在每个类中创建一个新属性。由于它们是static,因此您总是(几乎总是,是的反射)将使用特定的类来访问它们,因此您将指定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或静态只读变量。

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

两者的用法相同:

string name = MyClass.Name;

的主要优点(和缺点)const是在编译代码时,所有对它的引用实际上都被它的值替换。这意味着它会快一点,但是如果你改变了它的值,你将需要重新编译所有引用它的代码。

于 2012-06-22T19:23:43.473 回答
2

每当您编写 C# 代码时,请始终检查您的方法和属性 getter/setter 代码是否对类的其他实例成员有任何作用。如果没有,请务必应用static关键字。当然这里的情况,它可以轻松解决您的问题。

我真正发布这个问题的原因是在某些答案中存在一些语言偏见。不能对空对象调用实例方法的 C# 规则是特定的 C# 语言规则。毫无疑问,这是一个非常明智的方法,它确实有助于解决 NullReferenceExceptions,它们是在调用站点而不是在很难诊断this引用为 null 的方法内部的某个地方引发的。

但这当然不是 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;
}
于 2012-06-23T15:27:46.640 回答
1

创建静态属性:

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

    public MyClass()
    {
    }
}

像这样得到它:

string name1 = MyClass.Name;
于 2012-06-22T19:12:50.957 回答
0

创建静态类或静态属性,您不必显式实例化它。

于 2012-06-22T19:13:19.743 回答
0

这是不可能的。作为Name一个实例属性,如果你有一个实例,你只能获取它的值。

另外,请注意,您不是在谈论参数,而是在谈论属性

于 2012-06-22T19:13:29.913 回答