类型未知的泛型

本文关键字:泛型 未知 类型 | 更新日期: 2023-09-27 18:12:46

我正在创建一个设置类,如下所示:

class Setting<T> {...}

我将所有设置存储在树中,如下所示:

class SettingsNode {
    public Setting<> setting;
    public Setting<> child;
    public Setting<> sibling;
}

由于<>。我可以有多种类型(Setting<int>, Setting<string>等),所以我不知道如何创建SettingsNode。

所以我改成:

class SettingsNode {
    public object setting;
    public object child;
    public object sibling;
}

但是我在转换到正确的类型时遇到了麻烦:

// Create a setting from a type name as text
SettingsNode tmp = new SettingsNode();
Type genericType = typeof(Setting<>);
Type[] typeArgs = { Type.GetType("System.Int32") };
Type cType = genericType.MakeGenericType(typeArgs);
tmp.setting = Activator.CreateInstance(cType);
// Here's where I have a problem
Type baseType = typeArgs[0];
((Setting<baseType>)(tmp.setting)).SomeFunction();

最后一行的错误是:

类型或命名空间名称'baseType'无法找到(您是否缺少using指令或程序集引用?)

我该怎么做?谢谢!

类型未知的泛型

一个选项是您可以继续使用dynamic关键字:

class SettingsNode
{
    public dynamic setting;
    public dynamic child;
    public dynamic sibling;
}

你可以这样做:

SettingsNode tmp = new SettingsNode();
tmp.setting = new Setting<int>();
tmp.setting.SomeFunction()

无需使用反射

为什么不设置:

class SettingsNode<T> {
    public Setting<T> setting;
    public Setting<T> child;
    public Setting<T> sibling;
}

如果你真的想走这条路,创建一个抽象的非泛型基类abstract class Setting和一个泛型Setting<T> : Setting

使用object而不是T定义非泛型中的所有常用方法,并将泛型作为适配器使用,该适配器调用super的方法/属性并在每个方法/属性中强制转换object<=>T。

我不认为泛型是正确的工具。泛型是一个编译时工具:如果在编译时不知道泛型类型,则没有比非泛型类型更有利的地方。

因此,除非您计划在代码中显式地使用Setting<Int32>Setting<String>,否则在这里使用泛型是没有意义的,您不妨坚持使用Setting

泛型不能这样做。
泛型的全部意义在于在类型系统内指定类型参数& &;在编译时。
您试图使这些字段没有已知类型。

相反,您应该使泛型类继承非泛型SettingsBase基类(或接口),并使该类型的字段。
调用代码可以将它们强制转换回实际的泛型类型。

如果Setting<T>定义了方法:

public T Transform(T value);

…你可以定义一个Setting<?>类型的字段,那么Transform会接受什么类型的参数?object是所有可能的T所共有的唯一类型。这样就失去了类型安全性。Transform返回什么类型的参数?同样,object,所以你必须强制转换所有内容

您应该将每个泛型类实例视为不同的类型:手动将Setting<T>类复制为SettingOfInt32并将所有T替换为Int32,这可以通过泛型语法Setting<Int32>自动完成。因此,不要认为Setting<Int32>Setting<String>都是Setting<T>,而应该认为SettingOfInt32SettingOfString都是从Object派生出来的。

想象一下,您希望能够在字段中放置类型为SettingOfInt32SettingOfString的对象,该字段必须具有什么类型?当然,是两种类型都通用的类型,比如object。现在你可能意识到,如果你想调用SettingOfInt32SettingOfString中存在的任何方法,你应该将该方法移动到一个公共基类中,例如Setting。那么您的字段类型必须是Setting,并且一切都很好。

public class Setting<T> : Setting { }
public abstract class Setting { }

这也迫使您处理类似上面的Transform示例的事情。您是否将其包含在公共基类中?也许是,也许不是。你用什么类型代替T ?也许是object,也许是别的什么。你做显式类型检查吗?可能…

如前所述,我认为不应该在这种情况下使用泛型。如果你想要/需要坚持它,你可以尝试使用具有多个泛型的类。

public class SettingsNode<TSetting, TChild, TSibbling>
{
    public Setting<TSetting> Setting { get; set; }
    public Setting<TChild> Child { get; set; }
    public Setting<TSibbling> Sibling { get; set; }
}
public class Setting<T>
{
    public T Value;
}
SettingsNode<String, int, int> node = new SettingsNode<string, int, int>();
node.Sibling = new Setting<int>();
node.Setting = new Setting<string>();
node.Child = new Setting<int>();

有一次当我不知道该期待什么类型时,我采用了另一种方法。这涉及到动态关键字的使用,所以需要c# 4.0+