簡體   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