[英]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.