繁体   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