给定 T 的字符串表示形式,如何实例化 SomeObject<T> 的实例?

Posted

技术标签:

【中文标题】给定 T 的字符串表示形式,如何实例化 SomeObject<T> 的实例?【英文标题】:How can you instantiate an instance of SomeObject<T>, given the string representation of T? 【发布时间】:2015-10-24 12:20:15 【问题描述】:

我们有以下课程...

public class ValueHolder<T>

    public T Value  get; set; 
    public Type ValueType => typeof(T);

...我们当然可以这样实例化。

var foo = new ValueHolder<string>()  Value = "Hello World!" ;
var laa = new ValueHolder<int>()  Value = 44 ;

当我们使用 NewtonSoft 的 Json.NET 序列化 foolaa 时,我们得到以下输出...

// foo

    "Value": "Hello World!",
    "ValueType": "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"


// laa

    "Value": 44,
    "ValueType": "System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"

问题在于反序列化它,因为 Json.NET 不知道它指的是泛型,所以它爆炸了。因此,我需要为这个类编写一个自定义转换器。但是,我不确定如何实例化一个新的通用实例,其中 ValueHolder 中用于“T”的数据类型以字符串形式存储在 ValueType 中。

更多信息

我实际上是在尝试序列化/反序列化 Dictionary&lt;string,ValueHolder&lt;&gt;&gt; 的子类,其中每个实例的 ValueHolder 的 T 可能不同(当然你不能这样做,我实际上是在子类化 Dictionary&lt;string,object&gt; 然后把将 ValueHolder 解析为“对象”),所以我认为正确的方法是将转换器放在字典上而不是 ValueHolder 本身上。

【问题讨论】:

不清楚你在问什么。你想从字符串形式的类型中创建一个通用实例(类的)(??).. ???对我来说毫无意义。也许举个例子可以说明问题。 见这里:***.com/a/1371756/261050 @Maarten,MakeGenericType 看起来很有趣!用一个例子把它放在一个答案中,你就会得到荣誉。 【参考方案1】:

要从程序集名称创建新对象,您可以使用:

Activator.CreateInstance() 方法。请参阅此处的文档:

https://msdn.microsoft.com/en-us/library/system.activator.createinstance%28v=vs.110%29.aspx

【讨论】:

我知道激活器。但这仍然没有回答如何激活泛型类型。例如,如果 ValueType 是 System.Int,那么我需要激活的类型是 ValueHolder。但我没有'int'。我有它的字符串表示,我也不想激活它。有意义吗? 您有一个使用ToString() 创建的字符串表示。听起来是个坏方法 你想做什么?我现在明白你的问题,但可能有更好的方法来解决这个问题 Aniket,很抱歉您不理解这一点。所有细节都清楚地在问题中。不,我没有像你说的那样打电话给 ToString。这就是 Json.NET 正在写的内容,因为类型信息需要写出,因为它是反序列化所需的。 Aniket,当我还没有提出建议时,我不确定您所说的“更好的方法”是什么。正如我所说,我有 JSON,我知道它代表一个 ValueHolder 类,其中 T 作为字符串存储在 ValueType 中。我需要将该 JSON 反序列化为 ValueHolder 的一个实例,其中 T 是一个字符串、一个 int32、一个 long 等等,具体取决于 ValueType 中的类型。我不确定如何更清楚。再次抱歉,我不能,但你的 cmets 对我来说没有意义。【参考方案2】:

把你的班级分成这样:

public class ValueTypeBase

    public Type ValueType  get; set; 


public class ValueHolder<T> : ValueTypeBase

    public T Value  get; set; 

并检查此代码:

var json = "\"Value\": \"Hello World!\",\"ValueType\": \"System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089\"";
var valueTypeBase = JsonConvert.DeserializeObject<ValueTypeBase>(json);
var newType = typeof(ValueHolder<>).MakeGenericType(valueTypeBase.ValueType);
var obj = JsonConvert.DeserializeObject(json, newType);

【讨论】:

我知道,但是您只需将泛型类型的 T 值(此处为“字符串”)放入 DeserializeObject 调用中。这就是问题所在... ValueType 表示 ValueHolder 中用于 T 的类型。如何根据其类型的字符串表示形式指定 T 的值(巧合的是,这里也是一个字符串。) @Amir:“字符串”类型是硬编码的。如果我理解正确,问题是让它动态化,因为它并不总是字符串 @MarqueIV 这里的json 字符串是ValueHolder&lt;string&gt; 的字符串表示形式,因此只有反序列化为ValueHolder&lt;string&gt; 类型才有意义。您无法将其转换为 ValueHolder&lt;T&gt; @Aniket,再一次,你错过了重点。我没有尝试将其转换为 ValueHolder,而是尝试将其转换为 ValueHolderin that instance,因为 ValueType 表示它是一个字符串。但是 ValueType 可能是 int32,在这种情况下,我需要将其反序列化为 ValueHolder。这就是我要问的...如何创建 ValueType 的实例,其中 T 存储为 ValueType 中的字符串表示形式。 嗨,阿米尔!这非常接近我的需要。 (我以前从未见过的 MakeGenericType。非常酷!)问题是虽然它确实给了我正确的 obj 实例,但我看不出它如何适合他们的转换器,因为看起来你在调用 DeserializeObject 两次原始字符串,而转换器使用阅读器。我想我可以先尝试缓存字符串。想法?同样,这绝对是正确的道路,但并不完全在那里。 (如果有任何区别,被序列化/反序列化的实际对象是 ValueHolder 的字典)【参考方案3】:

希望对您有所帮助。

将 ValueHolder 类更改为此:

public class ValueHolder<T>

    public ValueHolder()  
    public ValueHolder(T v)
    
        Value = v;
    
    public T Value  get; set; 
    public Type ValueType()  return typeof(T); 

然后使用此代码:

        Type d1 = typeof(ValueHolder<>);
        //Your type -> "System.String"
        Type typeArgs = System.Type.GetType("System.String", false, true);
        Type constructed = d1.MakeGenericType(typeArgs);
        //Your Value-> "Test Value"
        var o = Activator.CreateInstance(constructed, "Test Value");    

【讨论】:

【参考方案4】:

使用 System.Type.GetType 和 Type.MakeGenericType 重建值不是问题(感谢 Ali Amanzadegan 和 Maarten):

var foo = new ValueHolder<string>()  Value = "Hello World!" ;
var laa = new ValueHolder<int>()  Value = 44 ;
var daa = new ValueHolder<double>  Value = 123.999 ;
//
var jsonFoo = JsonConvert.SerializeObject(foo);
var jsonLaa = JsonConvert.SerializeObject(laa);
var jsonDaa = JsonConvert.SerializeObject(daa);
//
Console.WriteLine(String.Format("03132", jsonFoo, jsonLaa, jsonDaa, Environment.NewLine));
//
var o = JsonConvert.DeserializeObject<JObject>(jsonFoo);
var typeDescription = o.Properties().FirstOrDefault(p => p.Name == "ValueType");
var type = System.Type.GetType(typeDescription.Value.ToString());
var reFoo = (dynamic)JsonConvert.DeserializeObject(jsonFoo, typeof(ValueHolder<>).MakeGenericType(type));
Console.WriteLine(reFoo.Value);

输出:

Hello World!

对我来说,困难在于找到一种方法来提取函数/方法中的功能。我提出的一种可能的解决方案是再次使用 dynamic

private dynamic reconstructValueHolderFromJson(string json)

    var o = JsonConvert.DeserializeObject<JObject>(json);
    var typeDescription = o.Properties().FirstOrDefault(p => p.Name == "ValueType");
    var type = System.Type.GetType(typeDescription.Value.ToString());
    return JsonConvert.DeserializeObject(json, typeof(ValueHolder<>).MakeGenericType(type));

现在重建对象看起来像这样(打印值):

var reFoo = reconstructValueHolderFromJson(jsonFoo);
Console.WriteLine("0", reFoo.Value);

var reLaa = reconstructValueHolderFromJson(jsonLaa);
Console.WriteLine("0", reLaa.Value);

var reDaa = reconstructValueHolderFromJson(jsonDaa);
Console.WriteLine("0", reDaa.Value);

还有输出:

Hello World!
44
123,999

【讨论】:

你的 genericValueHolder 不是多余的吗?您只是在创建该类型的实例以返回并获取其类型。换句话说,您实际上并没有使用该实例。如果您删除 CreateInstance 部分并使用 typeof(...) 开始等号之后的行,那么您将拥有直接传递给 DeserializeObject 的类型。 好点 - 你是对的。我已经删除了这部分,因为保留的代码与 reconstructValueHolderFromJson 方法中的代码相同。您是否找到了获取ValueHolder&lt;T&gt; 类型对象的方法? 仍在努力。我实际上是在尝试序列化一个 dictionary> (我实际上是作为 Dictionary 实现的),我认为正确的方法是将转换器放在字典本身上。那样的话,我就不需要投了。我只是把结果塞进字典里。那些使用字典的人知道他们需要什么类型,所以他们可以照顾演员。如果我得到它的工作,我会发布更新。不过,这绝对是在正确的道路上。

以上是关于给定 T 的字符串表示形式,如何实例化 SomeObject<T> 的实例?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 JavaScript 中从字符串实例化一个类

如何最好地将字符串表示形式转换为 DbType?

如果使用实例化对象,来为类动态的添加一个 字符串形式的 方法。

如何实例化 Realm Results<T> 数组

如何使用给定的根视图控制器和初始视图控制器实例化情节提要?

LeetCode 43 字符串相乘