簡體   English   中英

當我傳遞HashSet時,為什么我的泛型類型方法認為T是system.object

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

所以我試圖弄清楚為什么我的通用方法會丟失類型信息。

當我傳遞反射數據時(在本例中為T = HashSet),代碼認為T = system.object。 當我傳遞非反射數據時,我的T正確鍵入為HashSet類型。

我的示例中唯一的區別是,在我的第一個調用中,我直接傳遞了一個HashSet,而在第二個調用中,我傳遞了具有HashSet屬性的對象,並使用反射來獲取HashSet並將其傳遞。

兩個HashSet都由完全相同的代碼執行,但是T每次都不同。

我設法解決了一個“變通方法”,在該方法中,我傳入了第二個參數(正確的類型),從那里我可以得到想要的結果。

但是,這似乎很棘手,我想知道為什么我必須執行此替代方法才能獲得所需的結果。

誰能解釋我為什么要觀察自己的觀察結果,以及如何每次都能使我的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));
    }
}

本示例產生以下結果:

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

類型參數是在編譯時而不是在執行時推斷出來的(通常-稍后再介紹)。

當您調用complex.DeepTrimAll() ,它等效於:

Ext.DeepTrimAll<Complex>(complex);

然后調用DeepTrimAll<Complex>(complex, typeof(Complex))

現在, Complex不實現IEnumerable<string> ,因此代碼進入了if語句的else分支,該DeepTrimAll遞歸調用DeepTrimAll ,如下所示:

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

現在這里value的編譯時類型是object ,因此類型推斷在編譯時開始 ,並將其轉換為:

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

這就是為什么要獲得輸出的原因。

現在,您可以在此處使用動態類型輸入:

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

不再將DeepTrimAll稱為擴展方法,因為動態類型無法處理擴展方法。 但是現在類型推斷是動態執行的,因此它將調用DeepTrimAll<HashSet<string>>(value, propType)

建議這樣做-我可能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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM