C# Generics and Type Checking

C#GenericsTypes

C# Problem Overview


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

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 T that is passed in and then use a switch statement?

C# Solutions


Solution 1 - C#

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)){...}

Solution 2 - C#

You can use typeof(T).

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

Solution 3 - C#

And, because C# has evolved, you can (now) use pattern matching.

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 Exception("Invalid type");
        }
    }
}

And again with switch expressions in C# 8.0, the syntax gets even more succinct.

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 Exception("Invalid type")
        }
    }
}

Solution 4 - C#

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

Solution 5 - C#

I hope you find this helpful:

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

https://dotnetfiddle.net/5qUZnt

Solution 6 - C#

The typeof operator...

typeof(T)

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

https://stackoverflow.com/questions/298976/c-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"));

Solution 7 - C#

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.

Solution 8 - C#

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):

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.

Solution 9 - C#

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.

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.

Solution 10 - C#

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.

Solution 11 - C#

How about this :

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJonView Question on Stackoverflow
Solution 1 - C#jonniiView Answer on Stackoverflow
Solution 2 - C#bdowdenView Answer on Stackoverflow
Solution 3 - C#KitView Answer on Stackoverflow
Solution 4 - C#JaredParView Answer on Stackoverflow
Solution 5 - C#JaiderView Answer on Stackoverflow
Solution 6 - C#Robert HarveyView Answer on Stackoverflow
Solution 7 - C#wompView Answer on Stackoverflow
Solution 8 - C#JohnView Answer on Stackoverflow
Solution 9 - C#JoshBerkeView Answer on Stackoverflow
Solution 10 - C#mqpView Answer on Stackoverflow
Solution 11 - C#BertView Answer on Stackoverflow