C# 泛型和类型检查

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/982952/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 04:52:02  来源:igfitidea点击:

C# Generics and Type Checking

c#genericstypes

提问by Jon

I have a method that uses an IList<T>as a parameter. I need to check what the type of that Tobject is and do something based on it. I was trying to use the Tvalue, but the compiler does not not allow it. My solution is the following:

我有一个使用 anIList<T>作为参数的方法。我需要检查该T对象的类型并根据它做一些事情。我试图使用该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");
        }
    } 
}

There has to be a better way to do this. Is there some way I can check the type of Tthat is passed in and then use a switchstatement?

必须有更好的方法来做到这一点。有什么方法可以检查T传入的类型然后使用switch语句吗?

采纳答案by jonnii

You could use overloads:

您可以使用重载:

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

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

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

Or you could inspect the type of the generic parameter:

或者您可以检查泛型参数的类型:

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

回答by Robert Harvey

The typeof operator...

运算符的类型...

typeof(T)

... won't work with the c# switch statement. But how about this? The following post contains a static class...

... 不适用于 c# switch 语句。但这又如何呢?以下帖子包含一个静态类...

Is there a better alternative than this to 'switch on type'?

有比这更好的选择来“打开类型”吗?

...that will let you write code like this:

...这会让你编写这样的代码:

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"));

回答by bdowden

You can use typeof(T).

您可以使用typeof(T).

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

回答by JoshBerke

You can do typeOf(T), but I would double check your method and make sure your not violating single responsability here. This would be a code smell, and that's not to say it shouldn't be done but that you should be cautious.

你可以这样做typeOf(T),但我会仔细检查你的方法,并确保你没有在这里违反单一责任。这将是一种代码异味,并不是说不应该这样做,而是说您应该谨慎。

The point of generics is being able to build type-agnostic algorthims were you don't care what the type is or as long as it fits within a certain set of criteria. Your implementation isn't very generic.

泛型的要点是能够构建与类型无关的算法,只要您不关心类型是什么,或者只要它符合特定的一组标准。您的实现不是很通用。

回答by womp

There is no way to use the switch statement for what you want it to do. The switch statement must be supplied with integral types, which does not include complex types such as a "Type" object, or any other object type for that matter.

没有办法使用 switch 语句来完成你想要它做的事情。switch 语句必须提供整数类型,不包括复杂类型,例如“类型”对象,或任何其他与此相关的对象类型。

回答by mqp

Your construction completely defeats the purpose of a generic method. It's ugly on purpose because there must be a better way to achieve what you're trying to accomplish, although you haven't given us quite enough information to figure out what that is.

您的构造完全违背了泛型方法的目的。这是故意的丑陋,因为必须有更好的方法来实现你想要完成的目标,尽管你没有给我们足够的信息来弄清楚那是什么。

回答by JaredPar

By default know there is not a great way. Awhile back I got frustrated with this and wrote a little utility class that helped out a bit and made the syntax a bit cleaner. Essentially it turns the code into

默认情况下知道没有什么好方法。不久前,我对此感到沮丧,并编写了一个小实用程序类,它有所帮助并使语法更简洁。本质上它把代码变成

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

Full blog post and details on the implementation are available here

完整的博客文章和有关实施的详细信息可在此处获得

回答by John

For everyone that says checking types and doing something based on the type is not a great idea for generics I sort of agree but I think there could be some circumstances where this perfectly makes sense.

对于那些说检查类型并根据类型做一些事情对于泛型来说不是一个好主意的人,我有点同意,但我认为可能在某些情况下这完全有意义。

For example if you have a class that say is implemented like so (Note: I am not showing everything that this code does for simplicity and have simply cut and pasted into here so it may not build or work as intended like the entire code does but it gets the point across. Also, Unit is an enum):

例如,如果你有一个这样实现的类(注意:为了简单起见,我没有展示这段代码所做的一切,只是简单地剪切并粘贴到这里,所以它可能不会像整个代码那样构建或工作,但是它得到了重点。另外,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();
}

So in summary, I think there are valid reasons why you might want to check to see what type the generic is, in order to do something special.

总而言之,我认为您可能想要检查泛型是什么类型,以便做一些特殊的事情,这是有正当理由的。

回答by Bert

How about this :

这个怎么样 :

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

回答by Jaider

I hope you find this helpful:

我希望你觉得这有用:

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

https://dotnetfiddle.net/5qUZnt

https://dotnetfiddle.net/5qUZnt