Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pattern match a generic Type

Tags:

c#

generics

I'd like to detect whether a type matches a certain Type pattern. The pattern in this case is Expression<Func<T, bool>>, where T can match any type (the standard interpretation).

Given this pattern,

Expression<Func<string, bool>> v;  //matches
Expression<string> v;  //doesn't match    
Expression<Func<string, string, bool>> v;  //doesn't match

My strategy is compare whether the beginning of the FullName of the test type matches the FullName of a generic type. If it does, peels a layer off via GetGenericArguments, then repeat the check. Code of the first check:

Type testType;

Type firstCheck = (System.Linq.Expressions.Expression<>);
bool isExpression = testType.FullName.StartsWith(firstCheck.FullName);

This seems to work, but rather ugly. Moreover, it is not a generic solution; each pattern requires its own checker function. Does anyone have a better solution?

A related question is how to best express a pattern?

like image 828
Candy Chiu Avatar asked May 15 '26 18:05

Candy Chiu


1 Answers

Use GetGenericTypeDefinition method instead.

Type testType;

Type firstCheck = typeof(System.Linq.Expressions.Expression<>);
bool isExpression = 
    (testType.IsGenericType && testType.GetGenericTypeDefinition() == firstCheck)
    || (!testType.IsGenericType && testType == firstCheck);

Update

Here is a recurrence method to check if type match given pattern:

private static bool IsTypeCompatibile(Type pattern, Type test, Type placeholder)
{
    if (pattern == test || pattern == placeholder)
        return true;

    if(pattern.IsGenericType)
    {
        if (!test.IsGenericType || pattern.GetGenericTypeDefinition() != test.GetGenericTypeDefinition())
            return false;

        var patternGenericTypes = pattern.GetGenericArguments();
        var testGenericTypes = test.GetGenericArguments();

        return patternGenericTypes.Length == testGenericTypes.Length
            && patternGenericTypes.Zip(testGenericTypes, (p, t) => IsTypeCompatibile(p, t, placeholder)).All(x => x);

    }

    return false;
}

Usage:

var pattern = typeof(Expression<Func<Placeholder, bool>>);

var type1 = typeof(Expression<Func<string, bool>>);
var type2 = typeof(Expression<string>);
var type3 = typeof(Expression<Func<string, string, bool>>);

Console.WriteLine(IsTypeCompatibile(pattern, type1, typeof(Placeholder)));
Console.WriteLine(IsTypeCompatibile(pattern, type2, typeof(Placeholder)));
Console.WriteLine(IsTypeCompatibile(pattern, type3, typeof(Placeholder)));

Prints

True
False
False

Of course, you need to define Placeholder class:

public class Placeholder
{ }
like image 148
MarcinJuraszek Avatar answered May 18 '26 09:05

MarcinJuraszek



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!