簡體   English   中英

如何使用反射獲取私有字段的值?

[英]How to get the value of private field using reflection?

我遇到了一個問題,我需要訪問類的私有字段。 例如:

class MyClass 
{
    private string someString;

    public MyClass( string someStringValue )
    {
        someString = someStringValue;
    }
}

如何在 MyClass 之外獲取 someString 的值?

更新:

抱歉,我不能在這里使用屬性,因為實際的生產代碼是受保護的。 我是一名 QA/Dev,我需要一種方法來將這些私有化以編寫用戶驗收測試。 所以我不能更改生產代碼。 你能幫我嗎?

正如其他人所說,由於該字段是私有的,因此您不應該嘗試使用普通代碼來獲取它。 唯一可以接受的情況是在單元測試期間,即便如此,您也需要一個很好的理由來這樣做(例如將私有變量設置為 null,以便異常塊中的代碼將被命中並可以進行測試)。

您可以使用類似下面的方法來獲取字段:

/// <summary>
/// Uses reflection to get the field value from an object.
/// </summary>
///
/// <param name="type">The instance type.</param>
/// <param name="instance">The instance object.</param>
/// <param name="fieldName">The field's name which is to be fetched.</param>
///
/// <returns>The field value from the object.</returns>
internal static object GetInstanceField(Type type, object instance, string fieldName)
{
    BindingFlags bindFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic
        | BindingFlags.Static;
    FieldInfo field = type.GetField(fieldName, bindFlags);
    return field.GetValue(instance);
}

所以你可以這樣稱呼它:

string str = GetInstanceField(typeof(YourClass), instance, "someString") as string;

同樣,這不應在大多數情況下使用。

你不能——你也不是故意的。 這是私人的 如果這是其他人的課程,那么顯然他們不希望您訪問該領域。 它是私有的這一事實允許他們稍后更改實現 - 他們可能最終將該值作為另一個變量的一部分,或者重命名,或者如果不再需要它來實現公共 API,則可能完全消失。

如果它是您自己的類並且您確定希望其他人能夠訪問它,只需使用屬性公開它:

public string SomeString { get { return someString; } }

編輯:看到您的評論后,您可以通過反射訪問私有字段……但是對於驗收測試,您不必這樣做。 您應該測試公共 API。 對於單元測試,有時彎曲規則是有意義的,並將類視為“白盒”而不是“黑盒”測試,但對於驗收測試,我肯定會堅持使用公共 API。

如果這沒有幫助,我建議您與生產代碼的開發人員交談:解釋您為什么要訪問,並要求他們通過屬性公開它。 他們可以將其設為內部屬性,並使用[InternalsVisibleTo]在您的測試程序集中訪問它。 我個人更喜歡使用反射 - 否則,如果生產代碼以完全有效的方式更改,您的測試將在不應該失敗時失敗。

我遇到一個問題,我需要訪問班級的私有領域。 例如:

class MyClass 
{
    private string someString;

    public MyClass( string someStringValue )
    {
        someString = someStringValue;
    }
}

我如何在MyClass之外獲取someString的值?

更新:

抱歉,由於實際的生產代碼受到保護,因此我無法在此處使用屬性。 我是QA / Dev,我需要一種方法來使這些人私有以編寫用戶驗收測試。 因此,我無法更改生產代碼。 你能幫我嗎?

這是一個工作的泛型版本,我可以得到它。

private static object GetInstanceField<T>(T instance, string fieldName)
{                
    BindingFlags bindFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
    FieldInfo field = typeof(T).GetField(fieldName, bindFlags);
    return field.GetValue(instance);
}

用法

var str = (string)GetInstanceField(instance, "someString");

如果它是您的類並且您想在類之外提供對它的訪問,請通過公共屬性公開它:

public string SomeString { get { return someString; } }

否則,不要在課堂之外使用它。 你不是故意的。

編輯

根據您的評論,您實際上是在對生產代碼進行 QA 測試,我會質疑在測試期間需要訪問私有值的原因。 你真的應該只需要測試公開暴露的屬性/方法來驗證一切是否按預期運行。

話雖如此,如果真的沒有辦法解決問題,您可以使用反射來獲取值。

如果您將它用於單元測試,我實現了 @dcp 答案的通用版本,它提供了以下附加功能:

  • 斷言該字段是否存在
  • 斷言值是否為“T”類型。
  • 將值轉換為“T”類型。

代碼:

private const BindingFlags BindFlags = BindingFlags.Instance 
                                       | BindingFlags.Public 
                                       | BindingFlags.NonPublic
                                       | BindingFlags.Static;

/// <summary>
/// Uses reflection to get the field value from an object.
/// </summary>
/// <param name="type">The instance type.</param>
/// <param name="instance">The instance object.</param>
/// <param name="fieldName">The field's name which is to be fetched.</param>
/// <returns>An instance of <see cref="T"/>.</returns>
internal static T GetInstanceField<T>(Type type, object instance, string fieldName)
{
    var field = type.GetField(fieldName, BindFlags);
    Assert.IsNotNull(field, string.Format("The field with name '{0}' does not exist in type '{1}'.", fieldName, type));
    var value = field.GetValue(instance);
    Assert.IsInstanceOfType(value, typeof(T), string.Format("The value of the field '{0}' is not of type '{1}'", fieldName, typeof(T)));
    return (T)value;
}

您可以使用此擴展方法。

public static class Extensions
{
    public static object GetFieldValue(this object instance, string fieldName)
    {
        const BindingFlags bindFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
        var field = instance.GetType().GetField(fieldName, bindFlags);
        return field == null ? null : field.GetValue(instance);
    }
}

除了@dcp 答案之外,將其轉換為通用函數會更容易...

internal static T GetInstanceField<T>(object instance, string fieldName)
{
BindingFlags bindFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic
    | BindingFlags.Static;
FieldInfo field = type.GetField(fieldName, bindFlags);
return field.GetValue(instance);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM