[英]How can I access property on object of type T, when T is a generic class with unknown parameter?
[英]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);
這是一個相當簡化的示例,但正如您所見,該系統允許在子類上調用來自父類的靜態函數,以返回子類的對象。
cjk和Haris 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
(或TestClass
或AbstractTestClass
)實例。 但是,如果您沒有 class 的長期實例,這可能不可行。
在這種情況下,您假設 T 是 TestClass 的子類。 TestClass 的子類將沒有靜態 int x。
泛型不支持與靜態成員相關的任何內容,因此不起作用。 我的建議是:不要讓它靜止。 假設該字段真正與特定的T
,您還可以使用反射:
return (int) typeof(T).GetField("x").GetValue(null);
但我不推薦它。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.