我想做什么:使用Activator动态创建一个对象(可以是任何类型)并将其传递给序列化JSON的方法。注意:我已经查看了No type inference with generic extension method但是这并没有给我任何有关如何解决这个问题的有用信息。
编辑:使用.NET 3.5 Framework
问题:我收到的对象可能是一个数组(例如int []),当我使用泛型来键入接收到的对象时,我得到一个对象[]而不是int []。
代码示例:
class Program
{
static void Main(string[] args)
{
object objArr = new int[0];
int[] intArr = new int[0];
string arrS = "[1,2]";
object objThatIsObjectArray = Serialize(objArr, arrS);//I want this to evaluate as int[]
object objThatIsIntArray = Serialize(intArr, arrS);//this evaluates as int[]
Console.Read();
}
public static object Serialize<T>(T targetFieldForSerialization, string value)
{
return value.FromJson<T>();
}
}
public static class JSONExtensions
{
public static TType FromJson<TType>(this string json)
{
using (var ms = new MemoryStream(Encoding.Default.GetBytes(json)))
{
var ser = new DataContractJsonSerializer(typeof(TType));
var target = (TType)ser.ReadObject(ms);
ms.Close();
return target;
}
}
}
答案 0 :(得分:5)
没有自动的方法,如果您“认为”某个对象可能是int[]
,您可以尝试使用as
进行投射并检查结果是否为空;
static void Main(string[] args)
{
object objArr = new int[0];
string arrS = "[1,2]";
int[] testArr = objArr as int[];
if(testArr != null)
object objThatIsIntArray = Serialize(testArr, arrS);//this evaluates as int[]
else
object objThatIsObjectArray = Serialize(objArr, arrS); //this evaluates as object because it could not figure out what it was.
Console.Read();
}
如果您知道该类型只能是少数选择中的一种,您可以将其与其他if
测试链接起来,每种类型一个。
这种模式在处理接口时非常常见,例如,这里是LINQ的Count()
方法在内部实现的方式,它检查类是否实现ICollection<TSource>
或ICollection
所以它可以使用该接口的Count
属性。
public static int Count<TSource>(this IEnumerable<TSource> source)
{
if (source == null)
{
throw Error.ArgumentNull("source");
}
ICollection<TSource> collection = source as ICollection<TSource>;
if (collection != null)
{
return collection.Count;
}
ICollection collection2 = source as ICollection;
if (collection2 != null)
{
return collection2.Count;
}
int num = 0;
checked
{
using (IEnumerator<TSource> enumerator = source.GetEnumerator())
{
while (enumerator.MoveNext())
{
num++;
}
}
return num;
}
}
另一个选项是使用.GetType()
来获取Type并将其传递给而不是隐式检测它,尽管我不确切知道如何处理FromJson
的返回类型我的头顶。
class Program
{
static void Main(string[] args)
{
object objArr = new int[0];
int[] intArr = new int[0];
string arrS = "[1,2]";
object objThatIsObjectArray = Serialize(objArr.GetType(), arrS);//this evaluates as int[]
object objThatIsIntArray = Serialize(intArr.GetType(), arrS);//this evaluates as int[]
Console.Read();
}
public static object Serialize<T>(Type type, string value)
{
return value.FromJson(type);
}
}
public static class JSONExtensions
{
public static object FromJson(this string json, Type type)
{
using (var ms = new MemoryStream(Encoding.Default.GetBytes(json)))
{
var ser = new DataContractJsonSerializer(type);
var target = ser.ReadObject(ms);
ms.Close();
return target;
}
}
}
答案 1 :(得分:2)
预警:这个答案描述的技术可能不是基于您的示例代码的最佳选择,但了解类似情况很有用。
如果要基于运行时类型绑定到适当的方法,C#4.0+可以做到这一点(dynamic
类型)。在您的情况下,您希望基于第一个参数绑定类型参数T
,因此只需将类型为dynamic
的值作为第一个参数传递:
class Program
{
static void Main(string[] args)
{
dynamic objArr = new object[0];
dynamic intArr = new int[0];
int[] typedIntArr = new int[0];
string arrS = "[1,2]";
Serialize(objArr, arrS); // dynamic call site
Serialize(intArr, arrS); // dynamic call site
Serialize(typedIntArr, arrS); // regular static call
}
public static object Serialize<T>(T targetFieldForSerialization, string value)
{
Console.WriteLine("Type: " + typeof(T).Name);
return value.FromJson<T>();
}
}
使用动态参数调用Serialize
时,编译器将发出动态调用站点。现在,这是什么意思?
动态调用站点根据参数的运行时类型(以及可能的预期返回类型)评估调用并绑定到适当的方法。当调用完成后,绑定器将查看参数,检查它们的实际类型,然后确定要调用哪个方法,在泛型方法的情况下,确定要传递的类型参数。结果在我上面的代码片段的输出中很明显:
Type: Object[]
Type: Int32[]
Type: Int32[]
你可能认为这听起来像是一个非常重要的操作,而且确实如此。绑定器必须应用标准C#绑定规则来解析正确的方法。在知道所涉及的所有运行时类型之前,它通常甚至不能知道所有可能的候选方法。幸运的是,.NET中的动态调用站点通常不会为每个调用完成整个过程。动态调用站点记住过去调用的细节:当调用发生时,调用站点将根据过去的参数类型组合检查当前参数类型,如果找到匹配,它将调用之前调用的相同方法(使用相同的泛型类型参数)。这些检查和目标方法调用都会被编译,这有助于提高性能,您可以从JIT优化中受益。
现在,调用网站的缓存(它的“内存”)效果如何?这取决于参数类型的变化频率,以及它在整个生命周期中遇到的不同组合。动态语言运行时使用三个级别的缓存,因此在大多数情况下,您可以获得相当可观的性能 - 不是静态类型所能获得的,但可能比在每次调用时使用反射更好。在大多数情况下,调用网站最终将构建规则,如果您自己编写规则,则看起来像这样:
__Serialize(/* object[] */ objArr, arrS);
__Serialize(/* int[] */ intArr, arrS);
Serialize(typedIntArr, arrS);
...
private static object __Serialize(object arg1, string arg2) {
// These rules get updated as the type of 'arg1' changes:
if (arg1 is object[]) {
return Serialize<object[]>((object[])arg1, arg2);
}
else if (arg1 is int[]) {
return Serialize<int[]>((int[])arg1, arg2);
}
else {
// 1) Figure out the right method to call based on the type of 'arg1'
// 2) Update this set of rules
// 3) Call the newly bound method and return its result
}
}
所以,这一切都很吸引人,但这是你最好的选择吗?基于您问题中的示例代码,可能不是。为什么?在您的示例中,看起来您拥有TType
泛型参数的唯一原因是您可以捕获相应的Type
并将其用于反射(或者更确切地说,DataContractJsonSerializer
可以用它来反射)。当然,最简单的方法是在参数上调用GetType()
,这就是为什么Scott的第二个例子是这个特定情况的理想解决方案。如果你实际上不需要浪费动态绑定的开销是没有意义的(注意他完全删除了泛型参数)。但是,在某些时候,您可能会发现自己处于类似的情况,您真正可以从动态绑定中受益,当这个时间到来时,这些信息应该证明是有用的。
答案 2 :(得分:1)
我不确定我是否理解你的问题。我将假设您要做的是反序列化某些表示某种对象的数组的json,并且您希望将此反序列化的输出强制类型化为T类型的数组。
这意味着你在使用Deserialize方法时已经知道了type-argument应该是什么。如果您当时不知道这一点,系统如何提供强类型数组。毕竟,你必须要求它接收它。否则你会被那个对象数组困住。
我没有得到的是为什么你要传递第一个参数; Type type
,你们已经知道泛型参数的类型。所以我将删除类型参数,给我这样的东西:
所以这意味着:
public static T[] Deserialize<T>(string value)
{
return value.FromJson(value, typeof(T)) as T;
}
public static T[] DeserializeArray<T>(string value)
{
return Deserialize<T[]>(value);
}
并称之为:
int myInt = Deserialize<int>("1234");
int[] myIntArray1 = Deserialize<int[]>("[1, 2, 3, 4, 5]");
int[] myIntArray2 = DeserializeArray<int>("[1, 2, 3, 4, 5]");
我无法从您的代码或问题中看到它,因为我不知道我必须猜测的序列化器,但是如果由于retreiving对象[]而遇到反序列化对象时遇到问题那么你可能想用LINQ扩展来解决这个问题。
int[] myIntArray = new object[]{1,2}.OfType<int>().ToArray():
PS:据我理解术语,您可以将clr对象序列化为json,或将json反序列化为clr对象。所以你的问题是关于反序列化而不是序列化(这是你正在使用的词),如果我理解正确的话。?