繁体   English   中英

C#获取属性值而不创建实例?

[英]C# Get property value without creating instance?

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

我有这个课:

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

    public MyClass()
    {
    }
}

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

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

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

在舌头上,但仍然是正确的答案

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

是的,但是只能通过一些非常可怕的代码来创建一些IL,它使用DynamicMethod this null传递为null (您不会在属性中使用)。 样例代码:

// 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 ,则会发现它为null。

编辑:正如克里斯·辛克莱尔(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));

(但是再次,请不要!)

您可以将该属性设为静态

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

用法:

MyClass.Name;

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

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或static readonly变量。

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

两者的用法将相同:

 
 
 
 
  
  
  string name = MyClass.Name;
 
 
  

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

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

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

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

我之所以真正提出这个问题,是因为某些答案在工作中存在语言偏见。 您不能在空对象上调用实例方法的C#规则是特定的C#语言规则。 毫无疑问,这是一个非常明智的选择,它确实有助于对NullReferenceExceptions进行故障排除,它们是在调用站点引发的,而不是在很难诊断引用为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;
}

创建一个静态属性:

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

    public MyClass()
    {
    }
}

像这样得到它:

string name1 = MyClass.Name;

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

这是不可能的。 由于Name是实例属性,因此只有拥有实例时才能获取其值。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM