簡體   English   中英

C# Generics 和類型檢查

[英]C# Generics and Type Checking

我有一個使用IList<T>作為參數的方法。 我需要檢查T object 的類型,並根據它做一些事情。 我試圖使用T值,但編譯器不允許。 我的解決方案如下:

private static string BuildClause<T>(IList<T> clause)
{
    if (clause.Count > 0)
    {
        if (clause[0] is int || clause[0] is decimal)
        {
           //do something
        }
        else if (clause[0] is String)
        {
           //do something else
        }
        else if (...) //etc for all the types
        else
        {
           throw new ApplicationException("Invalid type");
        }
    } 
}

必須有更好的方法來做到這一點。 有什么方法可以檢查傳入的T類型,然后使用switch語句嗎?

您可以使用重載:

public static string BuildClause(List<string> l){...}

public static string BuildClause(List<int> l){...}

public static string BuildClause<T>(List<T> l){...}

或者您可以檢查泛型參數的類型:

Type listType = typeof(T);
if(listType == typeof(int)){...}

您可以使用typeof(T)

private static string BuildClause<T>(IList<T> clause)
{
     Type itemType = typeof(T);
     if(itemType == typeof(int) || itemType == typeof(decimal))
    ...
}

默認情況下知道沒有什么好方法。 不久前,我對此感到沮喪,並編寫了一個小實用程序類,它有所幫助並使語法更簡潔。 本質上它把代碼變成

TypeSwitcher.Do(clause[0],
  TypeSwitch.Case<int>(x => ...),  // x is an int
  TypeSwitch.Case<decimal>(d => ...), // d is a decimal 
  TypeSwitch.Case<string>(s => ...)); // s is a string

完整的博客文章和有關實施的詳細信息可在此處獲得

而且,由於 C# 已經發展,您(現在)可以使用模式匹配

private static string BuildClause<T>(IList<T> clause)
{
    if (clause.Count > 0)
    {
        switch (clause[0])
        {
            case int x: // do something with x, which is an int here...
            case decimal x: // do something with x, which is a decimal here...
            case string x: // do something with x, which is a string here...
            ...
            default: throw new ApplicationException("Invalid type");
        }
    }
}

再次使用 C# 8.0 中的switch 表達式,語法變得更加簡潔。

private static string BuildClause<T>(IList<T> clause)
{
    if (clause.Count > 0)
    {
        return clause[0] switch
        {
            int x => "some string related to this int",
            decimal x => "some string related to this decimal",
            string x => x,
            ...,
            _ => throw new ApplicationException("Invalid type")
        }
    }
}

我希望你覺得這有用:

  • typeof(IList<T>).IsGenericType == true
  • typeof(IList<T>).GetGenericTypeDefinition() == typeof(IList<>)
  • typeof(IList<int>).GetGenericArguments()[0] == typeof(int)

https://dotnetfiddle.net/5qUZnt

運算符的類型...

typeof(T)

... 不適用於 c# switch 語句。 但這又如何呢? 以下帖子包含一個靜態類...

有比這更好的選擇來“打開類型”嗎?

...這會讓你編寫這樣的代碼:

TypeSwitch.Do(
    sender,
    TypeSwitch.Case<Button>(() => textBox1.Text = "Hit a Button"),
    TypeSwitch.Case<CheckBox>(x => textBox1.Text = "Checkbox is " + x.Checked),
    TypeSwitch.Default(() => textBox1.Text = "Not sure what is hovered over"));

對於那些說檢查類型並根據類型做一些事情對於泛型來說不是一個好主意的人,我有點同意,但我認為可能在某些情況下這完全有意義。

例如,如果你有一個這樣實現的類(注意:為了簡單起見,我沒有展示這段代碼所做的一切,只是簡單地剪切並粘貼到這里,所以它可能不會像整個代碼那樣構建或工作,但是它得到了重點。另外,Unit是一個枚舉):

public class FoodCount<TValue> : BaseFoodCount
{
    public TValue Value { get; set; }

    public override string ToString()
    {
        if (Value is decimal)
        {
            // Code not cleaned up yet
            // Some code and values defined in base class

            mstrValue = Value.ToString();
            decimal mdecValue;
            decimal.TryParse(mstrValue, out mdecValue);

            mstrValue = decimal.Round(mdecValue).ToString();

            mstrValue = mstrValue + mstrUnitOfMeasurement;
            return mstrValue;
        }
        else
        {
            // Simply return a string
            string str = Value.ToString() + mstrUnitOfMeasurement;
            return str;
        }
    }
}

...

public class SaturatedFat : FoodCountWithDailyValue<decimal>
{
    public SaturatedFat()
    {
        mUnit = Unit.g;
    }

}

public class Fiber : FoodCount<int>
{
    public Fiber()
    {
        mUnit = Unit.g;
    }
}

public void DoSomething()
{
       nutritionFields.SaturatedFat oSatFat = new nutritionFields.SaturatedFat();

       string mstrValueToDisplayPreFormatted= oSatFat.ToString();
}

所以總而言之,我認為您可能想要檢查泛型是什么類型,以便做一些特別的事情,這是有正當理由的。

沒有辦法使用 switch 語句來完成你想要它做的事情。 switch 語句必須提供整數類型,不包括復雜類型,例如“類型”對象,或任何其他與此相關的對象類型。

你可以做typeOf(T) ,但我會仔細檢查你的方法並確保你沒有在這里違反單一責任。 這將是一種代碼異味,並不是說不應該這樣做,而是說您應該謹慎。

泛型的要點是能夠構建與類型無關的算法,只要您不關心類型是什么,或者只要它符合特定的一組標准。 您的實現不是很通用。

您的構造完全違背了泛型方法的目的。 這是故意的丑陋,因為必須有更好的方法來實現你想要完成的目標,盡管你沒有給我們足夠的信息來弄清楚那是什么。

這個怎么樣 :

            // Checks to see if the value passed is valid. 
            if (!TypeDescriptor.GetConverter(typeof(T)).IsValid(value))
            {
                throw new ArgumentException();
            }

我的兩分錢:

如果您碰巧有一個返回通用值但沒有通用參數的通用方法,您可以使用default(T) + (T)(object) cast,連同C# 8 模式匹配/類型檢查(如指示在其他最近的答案中)。

例子:

private static T Parse<T>(string str)
{
    return default(T) switch
    {
        short => (T)(object)short.Parse(str),
        ushort => (T)(object)ushort.Parse(str),
        int => (T)(object)int.Parse(str),
        uint => (T)(object)uint.Parse(str),
        long => (T)(object)long.Parse(str),
        ulong => (T)(object)ulong.Parse(str),
        _ => throw new ArgumentException()
    };
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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