在C#上编译时,检查某些函数参数数据类型是否相等

时间:2010-02-25 09:48:29

标签: c# generics parameters types constraints

我有一些示例数据:

public struct Task
{
    public int INT;
    public string STRING;
    public DateTime? NULLABLEDATETIME;
}

功能,使用它:

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

以下是函数调用的示例:

Marry(t => t.INT, 1984);
Marry(t => t.NULLABLEDATETIME, DateTime.Now);
Marry(t => t.STRING, "SomeSting");

此代码有效,没问题。不幸的是,我们也可以调用下面的函数,因为String和int都是从对象类继承的:

Marry(t => t.INT, "SomeSting");

我想说编译器,第一个和第二个参数具有相同的数据类型:int -> intstring -> stringDateTime? -> DateTime?并在编译期间检查它。我试过这个:

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

这几乎可以工作,如果我尝试输入错误的数据,如Marry(t => t.INT, "SomeSting");,编译器会报错: 类型'string'不能在泛型类型或方法'Task.Marry(System.Linq.Expressions.Expression&gt;,T2)'中用作类型参数'T2'。没有从'string'到'int'的隐式引用转换。

但是此解决方案不适用于null。当我调用Marry(t => t.NULLABLEDATETIME, null);时,编译器说: 方法'Marry(System.Linq.Expressions.Expression&gt;,T2)'的类型参数无法从用法中推断出来。尝试明确指定类型参数。

为什么呢?我已经知道数据类型:DateTime?。我不想明确地呼叫Marry<DateTime?>(t => t.NULLABLEDATETIME, null);。我该怎么做 - 还是有另一种方法可以在编译期间检查一些函数参数类型?

2 个答案:

答案 0 :(得分:3)

为什么不简单地用一个通用类型参数替换T1T2?然后不需要推断第二个参数类型 - 它不必无论如何,因为它必须与第一个参数的相同。

看起来像这样:

变式1

public KeyValuePair<Expression<Func<Task, T>>, T> 
    Marry<T>(
       Expression<Func<Task, T>> key, 
       T value) {
    return new KeyValuePair<Expression<Func<Task, T>>, T>(key, value);
}

我不建议这样做,但是如果你希望类型推断“贪婪地”仅根据第一个参数来确定表达式的类型(这样你就会得到错误“无法从'string'转换为' DateTime?'),你可以讨论方法:

变式2

public static Func<T, KeyValuePair<Expression<Func<Task, T>>, T> >
    Marry<T>(
        Expression<Func<Task, T>> key) {
    return (value=>new KeyValuePair<Expression<Func<Task, T>>, T>(key, value));
}
//usage:
Marry(t => t.NULLABLEDATETIME)("test"); 
//error: Delegate 'System.Func<System.DateTime?,System.Collections.Generic.KeyValuePair<System.Linq.Expressions.Expression<System.Func<UserQuery.Task,System.DateTime?>>,System.DateTime?>>' has some invalid arguments
//  - Argument 1: cannot convert from 'string' to 'System.DateTime?'

该错误消息基本上涵盖了它。但是,API不太可读,并且使用大量嵌套的泛型类型会使事情变得不那么可读。具有基本相同行为但较短错误消息的较长变体可能如下所示:

变体3

public class MarriagePartner<T> {
    readonly Expression<Func<Task, T>> key;
    public MarriagePartner(Expression<Func<Task, T>> key) { this.key = key; }
    public  KeyValuePair<Expression<Func<Task, T>>, T> With(T value) {
        return new KeyValuePair<Expression<Func<Task, T>>, T>(key, value);
    }
}

public static MarriagePartner<T> Marry2<T>(Expression<Func<Task, T>> key) { 
    return new MarriagePartner<T>(key);
}

//Usage:
Marry2(t => t.NULLABLEDATETIME).With("test"); 
//error: The best overloaded method match for 'UserQuery.MarriagePartner<System.DateTime?>.With(System.DateTime?)' has some invalid arguments
//  - Argument 1: cannot convert from 'string' to 'System.DateTime?'

该错误消息更具体一些。我认为Variant 1的错误消息就足够了,而且它是我使用的(KISS和所有) - 但是如果你真的想要那个转换错误消息,Variant 3有最容易理解的(如果更长)实现和大多数友好的错误信息。

答案 1 :(得分:1)

您可以将null转换为某种类型

(string)null, (object)null