简体   繁体   English

当我传递HashSet时,为什么我的泛型类型方法认为T是system.object

[英]Why does my generic type method think T is system.object when I'm passing in a HashSet

So I'm trying to figure out why my generic method is losing type information. 所以我试图弄清楚为什么我的通用方法会丢失类型信息。

When I pass in reflected data (in this example T = HashSet) the code thinks T = system.object. 当我传递反射数据时(在本例中为T = HashSet),代码认为T = system.object。 When I pass in non-reflected data, my T is correctly typed as HashSet type. 当我传递非反射数据时,我的T正确键入为HashSet类型。

The only difference in my example is that in my first call I am passing directly a HashSet, and in my second call I am passing in an object with a HashSet property, and using reflection to get the HashSet and pass it in. 我的示例中唯一的区别是,在我的第一个调用中,我直接传递了一个HashSet,而在第二个调用中,我传递了具有HashSet属性的对象,并使用反射来获取HashSet并将其传递。

Both HashSets get executed by the exact same code, yet T is different each time. 两个HashSet都由完全相同的代码执行,但是T每次都不同。

I've managed to hack together a "workaround" where I pass in as a second parameter, the correct type, and from there I can get my desired results. 我设法解决了一个“变通方法”,在该方法中,我传入了第二个参数(正确的类型),从那里我可以得到想要的结果。

However, this seems hacky and I'd like to know why I have to do this workaround to get my desired results. 但是,这似乎很棘手,我想知道为什么我必须执行此替代方法才能获得所需的结果。

Can anybody explain why I am observing what I'm observing and how I can get my T type to be correct each and every time without passing in the type twice? 谁能解释我为什么要观察自己的观察结果,以及如何每次都能使我的T型正确,而又不将其传递两次?

using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;

public class Program
{
    public static void Main()
    {
        var simple = new HashSet<string>();
        simple.Add("1");
        simple.Add(" 1 ");
        Console.WriteLine("Simple Before => " + JsonConvert.SerializeObject(simple));
        simple = simple.DeepTrimAll();
        Console.WriteLine("Simple After => " + JsonConvert.SerializeObject(simple));
        Console.WriteLine();
        var complex = new Complex();
        complex.HS = new HashSet<string>();
        complex.HS.Add("1");
        complex.HS.Add(" 1 ");
        Console.WriteLine("Complex Before => " + JsonConvert.SerializeObject(complex));
        complex = complex.DeepTrimAll();
        Console.WriteLine("Complex After => " + JsonConvert.SerializeObject(complex));
    }
}

public class Complex
{
    public HashSet<string> HS
    {
        get;
        set;
    }
}

public static class Ext
{
    public static T DeepTrimAll<T>(this T context, Type t)where T : class
    {
        if (context is IEnumerable<string>)
        {
            var type = typeof (T);
            Console.WriteLine("T = " + type.ToString() + " , t = " + t.ToString());
            var list = new List<string>(context as IEnumerable<string>);
            for (int i = 0; i < list.Count(); i++)
            {
                list[i] = list[i].Trim();
            }

            var res = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(list), t);
            return (T)res;
        }

        var properties = typeof (T).GetProperties().Where(x => x.CanRead && x.CanWrite).ToList();
        foreach (var propertyInfo in properties)
        {
            var propType = propertyInfo.PropertyType;
            var value = propertyInfo.GetValue(context, null);
            if (propType == typeof (string))
            {
                if (!string.IsNullOrWhiteSpace(value.ToString()))
                {
                    propertyInfo.SetValue(context, value.ToString().Trim());
                }
            }
            else if (!propType.IsEnum && !propType.IsPrimitive)
            {
                var newValue = value.DeepTrimAll(propType);
                propertyInfo.SetValue(context, newValue);
            }
        }

        return context;
    }

    public static T DeepTrimAll<T>(this T context)where T : class
    {
        return context.DeepTrimAll(typeof (T));
    }
}

This example produces the following results: 本示例产生以下结果:

Simple Before => ["1"," 1 "]
T = System.Collections.Generic.HashSet`1[System.String] , t = System.Collections.Generic.HashSet`1[System.String]
Simple After => ["1"]

Complex Before => {"HS":["1"," 1 "]}
T = System.Object , t = System.Collections.Generic.HashSet`1[System.String]
Complex After => {"HS":["1"]}

dotNetFiddle dotNetFiddle

Type arguments are inferred at compile time, not at execution time (usually - see later). 类型参数是在编译时而不是在执行时推断出来的(通常-稍后再介绍)。

When you call complex.DeepTrimAll() , it's equivalent to: 当您调用complex.DeepTrimAll() ,它等效于:

Ext.DeepTrimAll<Complex>(complex);

That then calls DeepTrimAll<Complex>(complex, typeof(Complex)) . 然后调用DeepTrimAll<Complex>(complex, typeof(Complex))

Now Complex doesn't implement IEnumerable<string> , so the code goes into the else branch of your if statement, which calls DeepTrimAll recursively, like this: 现在, Complex不实现IEnumerable<string> ,因此代码进入了if语句的else分支,该DeepTrimAll递归调用DeepTrimAll ,如下所示:

var propType = propertyInfo.PropertyType;
var value = propertyInfo.GetValue(context, null);
...
var newValue = value.DeepTrimAll(propType);

Now the compile-time type of value here is object , so type inference kicks in at compile time and converts that to: 现在这里value的编译时类型是object ,因此类型推断在编译时开始 ,并将其转换为:

var newValue = DeepTrimAll<object>(value, propType);

That's why you're getting the output you are. 这就是为什么要获得输出的原因。

Now, you could use dynamic typing here instead: 现在,您可以在此处使用动态类型输入:

var propType = propertyInfo.PropertyType;
dynamic value = propertyInfo.GetValue(context, null);
...
var newValue = DeepTrimAll(value, propType);

That's no longer calling DeepTrimAll as an extension method, because dynamic typing doesn't handle extension methods. 不再将DeepTrimAll称为扩展方法,因为动态类型无法处理扩展方法。 But now type inference is performed dynamically, so it will call DeepTrimAll<HashSet<string>>(value, propType) . 但是现在类型推断是动态执行的,因此它将调用DeepTrimAll<HashSet<string>>(value, propType)

I would not suggest doing this though - I'd probably change your DeepTrimAll(T context, Type t) to just DeepTrimAll(object context, Type t) and always use t instead of typeof(T) . 建议这样做-我可能DeepTrimAll(T context, Type t)您的DeepTrimAll(T context, Type t)更改为DeepTrimAll(object context, Type t)始终使用t代替typeof(T)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用System.Object时,对通用方法实现new()约束 - Fulfilling a new() constraint on a generic method when using System.Object 为什么没有在System.Object中放入Clone方法? - Why wasn't a Clone method put in System.Object? 通用方法无法将“System.Object”类型的对象转换为 X 类型 - Generic Method Unable to cast object of type 'System.Object' to type X 为什么类型是System.Object? - Why the type is System.Object here? LINQ to Entities无法识别方法&#39;System.Object Parse(System.Type,System.String)&#39; - LINQ to Entities does not recognize the method 'System.Object Parse(System.Type, System.String)' 什么时候特定类型实际上是System.Object? - When is a specific type actually a System.Object? 为什么 CreateSQLQuery 抛出异常“值 System.Object[] 不是类型,不能在此泛型集合中使用”? - Why CreateSQLQuery throwing exception "The value System.Object[] is not of type and cannot be used in this generic collection"? LINQ to SQL为什么在尝试将新对象添加到数据库时认为我的新对象为空? - Why does LINQ to SQL think think that my new object is null when I try to add it to the database? 在C#中将变量传递给具有system.object参数的方法 - passing a variable to a method with a system.object parameter in c# 将字符串转换为int时,特定的linq异常。 LINQ to Entities无法识别方法&#39;Int32 ToInt32(System.Object)&#39;m - Specific linq exception when converting string to int. LINQ to Entities does not recognize the method 'Int32 ToInt32(System.Object)' m
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM