簡體   English   中英

如何在泛型 class 中訪問 T 類型的 static 屬性?

[英]How can I access a static property of type T in a generic class?

我正在嘗試完成以下場景,即通用 TestClassWrapper 將能夠訪問它所組成的類的 static 個屬性(它們都將派生自 TestClass)。 就像是:

public class TestClass
{
    public static int x = 5;
}

public class TestClassWrapper<T> where T : TestClass
{
    public int test()
    {
        return T.x;
    }
}

給出錯誤:

“T”是“類型參數”,在給定上下文中無效。

有什么建議么?

你不能,基本上,至少不能沒有反思。

一種選擇是在構造函數中放置一個委托,以便創建實例的人可以指定如何獲取它:

var wrapper = new TestClassWrapper<TestClass>(() => TestClass.x);

如有必要,您可以通過反射來實現:

public class TestClassWrapper<T> where T : TestClass
{
    private static readonly FieldInfo field = typeof(T).GetField("x");

    public int test()
    {
        return (int) field.GetValue(null);
    }
}

(如有必要,添加適當的綁定標志。)

這不是很好,但至少你只需要查看一次字段......

當然你可以這樣寫:

public int test() 
{ 
    return TestClass.x; 
} 

即使在一個重要的例子中,你也不能覆蓋一個靜態字段,所以總是從你已知的基類中調用它。

為什么不直接返回TestClass.x

T 是一種類型,而不是參數或變量,因此您不能從任何成員中選取任何值。 這是一個示例代碼。

public class UrlRecordService
{
    public virtual void SaveSlug<T>(T entity) where T : ISlugSupport
    {
        if (entity == null)
            throw new ArgumentNullException("entity");

        int entityId = entity.Id;
        string entityName = typeof(T).Name;
    }
}


public interface ISlugSupport
{
    int Id { get; set; }
}

另一個解決方案是簡單地使其成為靜態,並使用 T 上的new()約束來實例化對象。 然后您可以使用接口,並且包裝器可以從實現該接口的任何類中獲取屬性:

public interface XExposer
{
    Int32 X { get; }
}

public class TestClass : XExposer
{
    public Int32 X { get { return 5;} }
}

public class XExposerWrapper<T> where T : XExposer, new()
{
    public Int32 X
    {
        get { return new T().X; }
    }
}

實際上,您可以在 TestClassWrapper 上將其更改為public static Int32 X並簡單地將其作為Int32 fetchedX = XExposerWrapper<TestClass>.X;

盡管因為無論代碼調用它都必須給參數 T 提供相同的約束,所以此時包裝類是非常不必要的,因為調用代碼本身也可以只執行new T().X而不必打擾包裝器。

盡管如此,仍然有一些有趣的繼承模型可以使用這種結構。 例如, abstract class SuperClass<T> where T : SuperClass<T>, new()可以在其靜態函數中實例化和返回類型 T,有效地允許您創建適應子類的可繼承靜態函數(這將然后需要定義為class ChildClass : SuperClass<ChildClass> )。 通過在超類上定義protected abstract函數/屬性,您可以創建在任何繼承對象上應用相同邏輯的函數,但根據這些抽象的實現為該子類定制。 我將它用於表名和獲取查詢由子類實現的數據庫類。 由於這些屬性受到保護,它們也永遠不會暴露。

例如,在數據庫類上,實際的獲取邏輯放在一個中央抽象類中:

public abstract class DbClass<T> where T : DbClass<T>, new()
{
    protected abstract String FetchQuery { get; }

    protected abstract void Initialize(DatabaseRecord row);

    public static T FetchObject(DatabaseSession dbSession, Int32 key)
    {
        T obj = new T();
        DatabaseRecord record = dbSession.RetrieveRecord(obj.FetchQuery, key);
        obj.Initialize(row);
        return obj;
    }
}

和實施:

public class User : DbClass<User>
{
    public Int32 Key { get; private set;}
    public String FirstName { get; set;}
    public String LastName { get; set;}

    protected override String FetchQuery
    { get { return "SELECT * FROM USER WHERE KEY = {0}";} }

    protected override void Initialize(DatabaseRecord row)
    {
        this.Key = DatabaseSession.SafeGetInt(row.GetField("KEY"));
        this.FirstName = DatabaseSession.SafeGetString(row.GetField("FIRST_NAME"));
        this.LastName = DatabaseSession.SafeGetString(row.GetField("LAST_NAME"));
    }
}

這可以用作:

User usr = User.FetchObject(dbSession, userKey);

這是一個相當簡化的示例,但正如您所見,該系統允許在子類上調用來自父類的靜態函數,以返回子類的對象。

cjkHaris Hasan對問題的回答最正確。 然而,在這個評論中,OP 暗示他在 C# 中追求其他不太可能的東西:一種在派生的 class 中為 static 成員定義合同的方法。

沒有辦法嚴格定義它,但可以設置一個模式,該模式可能由基數 class(或接口)暗示; 例如:

public class TestClass
{
    private static int x;

    public virtual int StaticX => x;
}

或者如果不打算直接使用

public abstract class AbstractTestClass
{
    public abstract int StaticX {get;}
}

或者(我在這個人為的例子中的偏好)

public interface ITest
{
    int StaticX {get;}
}

在其他地方, StaticXxx成員的這種模式可能(松散地)與應該使用 static 字段(如上面的TestClass中)支持成員的實現相關聯。

有趣的是,通用包裝器可以將其(重新)公開為 static,因為通用靜態與所使用的每種類型都是隔離的。

public class TestClassWrapper<T> where T : ITest, new()
{
    private readonly static T testInstance = new T();

    public static int test() => testInstance.x;
}

這使用new()條件,但也可以使用關聯的 static 通用工廠模式來創建ITest (或TestClassAbstractTestClass )實例。 但是,如果您沒有 class 的長期實例,這可能不可行。

在這種情況下,您假設 T 是 TestClass 的子類。 TestClass 的子類將沒有靜態 int x。

泛型不支持與靜態成員相關的任何內容,因此不起作用。 我的建議是:不要讓它靜止。 假設該字段真正與特定的T ,您還可以使用反射:

return (int) typeof(T).GetField("x").GetValue(null);

但我不推薦它。

暫無
暫無

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

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