具有泛型返回的通用方法

时间:2013-11-12 14:19:06

标签: c#-4.0 generics methods lambda generic-method

好吧,我有以下情况:

public class Joins<TOuter, TInner, TResult>
{
    public Expression<Func<TOuter, object>> outerKeySelector;
    public Expression<Func<TInner, object>> innerKeySelector;
    public Expression<Func<TOuter, TInner, object>> resultSelector;

    public IEnumerable<TResult> r;

}

public class Test<T>
{
    public IEnumerable<TResult> Join<TInner, TResult>(
        Expression<Func<T, object>> outerKeySelector,
        Expression<Func<TInner, object>> innerKeySelector,
        Expression<Func<T, TInner, TResult>> resultSelector) where TInner : class
    {
        var join = new Joins<T, TInner, TResult>();
        join.innerKeySelector = innerKeySelector;
        join.outerKeySelector = outerKeySelector;

        return join.r;
    }
}

要创建联接方法,我依赖链接:http://msdn.microsoft.com/en-us/library/bb534644(v=vs.100).aspx

但是,当我尝试调用该方法时,无法识别 TInner ,使得该方法无效,我返回以下错误:

  

无法将表达式'type'转换为返回类型'TResult'

注意: '加入' 这个类纯粹是一个测试,没有一个是明确的, var'r' 仅用于测试,仅用于促进返回。

预期用途示例:

var test = new Test<User>().Join<Permission>(u => u.Id, p => p.IdUser, (u, p) => new { Id = u.Id , Area = p.Area });

更多详情:

如上所述,无法识别 TInner ,因此我无法拨打正确的方法。

作为测试,我这样做了:

var test = new Test<User>().Join<Permission>(u => u.Id, p => p.ToString(), (u, p) => new {Id = u.Id, Name = p.ToString()});

我知道 p.ToString() 不正确,但是没有识别所指示的类的属性(在Permission情况下),然后放入ToString()只是为了完成方法的写作。

修改

我需要在foreach / for

中使用结果

示例:

foreach(var obj in test)
{
    var id = obj.Id;
    var area = obj.Area;
    .
    .
    .
}

1 个答案:

答案 0 :(得分:0)

要使其有效,您应该执行以下操作:

 var test = new Test<User>().Join<Permission, object>(u => u.Id, p => p.IdUser, (u, p) => new { Id = u.Id, Area = p.Area });
 //                                           ^^ (resolve the second type argument as well)     

定义以下内容时:

public TResult Join<TInner, TResult>

然后你应该给编译器一个提示,显示哪个类型(而不是任何,提到类型参数:TInner,TResult),或者它必须以某种方式自动识别编译器(然后你可以从显式定义中省略它),但在你的情况下你必须明确地这样做。


看看this answer。尽管这个问题也与扩展方法有关,但是对于要解析的类型参数的逻辑有很好的解释。

修改

但是object,肯定不是最好的决定,因为你以后不能对该对象做任何事情(特别是,如果这是一个匿名类型)。

所以,你应该改变它,例如,通过以下方式:

    public class Test<T, TInner> where TInner : class where T : class 
    {
        public IEnumerable<TResult> Join<TResult>(

        ...

            Expression<Func<T, TInner, TResult>> resultSelector)

之后你可以按如下方式使用它:

var test = new Test<User, Permission>()
                .Join(u => u.Id, p => p.IdUser, (u, p) => new { Id = u.Id, Area = p.Area });

修改

如果您需要将TInnerTResult传递给Join方法,那实际上是不可能的。匿名类只能由编译器自动解析 - 您无法明确指定它。

那么,你可以解决它的唯一方法,如下:

var test = new Test<User>().Join<Permission, dynamic>(...

允许您使用LinQ .Where(方法使用结果,但是没有智能感知支持(除非您为Join输出创建具体类)。