繁体   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