I have some example data:
public struct Task
{
public int INT;
public string STRING;
public DateTime? NULLABLEDATETIME;
}
And function, which uses it:
public KeyValuePair<Expression<Func<Task, object>>, object> Marry(Expression<Func<Task, object>> key, object value)
{
return new KeyValuePair<Expression<Func<Task, object>>, object>(key, value);
}
Here is example of function call:
Marry(t => t.INT, 1984);
Marry(t => t.NULLABLEDATETIME, DateTime.Now);
Marry(t => t.STRING, "SomeSting");
This code works, no questions. Unfortunatly we can also call functions like below, because String and int are both inherited from object class:
Marry(t => t.INT, "SomeSting");
I want to say compiler, that first and second parameters have same data type: int -> int, string -> string, DateTime? -> DateTime? and check it during compilation. I tried this:
public KeyValuePair<Expression<Func<Task, T1>>, T2> Marry<T1, T2>(Expression<Func<Task, T1>> key, T2 value)
where T2 : T1
{
return new KeyValuePair<Expression<Func<Task, T1>>, T2>(key, value);
}
This almost works, and if I try to put wrong data like this Marry(t => t.INT, "SomeSting"); the compiler reports an error:
The type ‘string’ cannot be used as type parameter ‘T2’ in the generic type or method ‘Task.Marry(System.Linq.Expressions.Expression>, T2)’. There is no implicit reference conversion from ‘string’ to ‘int’.
But this solution does not work with null. When I call Marry(t => t.NULLABLEDATETIME, null); the compiler says:
The type arguments for method ‘Marry(System.Linq.Expressions.Expression>, T2)’ cannot be inferred from the usage. Try specifying the type arguments explicitly.
Why? I already know the data type: DateTime?. I don’t want to explicitly call Marry<DateTime?>(t => t.NULLABLEDATETIME, null);. How can I do this – or is there another way to check some function parameter types during compilation?
Why don’t you simply replace
T1andT2with a single generic type parameter? Then the second parameters type needn’t be inferred – which it doesn’t have to be anyhow since it must be identical to that of the first parameter.That would look like this:
Variant 1
I don’t recommend this, but if you want the type-inference to “greedily” determine the type of the expression based solely on the first parameter (so that you get the error “cannot convert from ‘string’ to ‘DateTime?’), you can curry the method:
Variant 2
That error message essentially covers it. However, the API isn’t too readable and the usage of heavily nested generic types makes things… less readable. A longer variant with essentially the same behavior but shorter error messages could look as follows:
Variant 3
That error message is a little more specific. I think Variant 1’s error message suffices and it’s what I’d use (KISS and all) – but if you Really want that casting error message, Variant 3 has the most understandable (if longer) implementation and most friendly error message.