首页新闻找找看学习计划

C#泛型问题:泛型方法中如何返回具体类型的实例

0
悬赏园豆:100 [已解决问题] 解决于 2015-08-27 20:01

在fake IUnitOfWork接口的一个方法时,遇到了一个泛型问题,不知如何解决。

这个方法在接口中是这样定义的:

public interface IUnitOfWork : IDisposable
{
    IQueryable<T> Set<T>() where T : class;
}

希望在实现这个接口方法时,直接返回一个具体类型的实例。

尝试这样实现:

public class FakeUnitOfWork : IUnitOfWork
{
    public IQueryable<T> Set<T>() where T : class
    {
        if (typeof(T) == typeof(AdUnit))
        {
            return new List<AdUnit>().AsQueryable();
        }
        else
        {
            throw new NotImplementedException();
        }
    }
}

编译出现下面的错误:

Error CS0266: Cannot implicitly convert type 'System.Linq.IQueryable<AdUnit>' to 'System.Linq.IQueryable<T>'. An explicit conversion exists (are you missing a cast?)

改为:

return new List<AdUnit>().AsQueryable<T>();

编译错误变为:

Error CS1929: 'List<AdUnit>' does not contain a definition for 'AsQueryable' and the best extension method overload 'Queryable.AsQueryable<T>(IEnumerable<T>)' requires a receiver of type 'IEnumerable<T>'	

请问如何解决这个问题?

C#
问题补充:

 new List<AdUnit>() 是提问时的简化。实际场景下,List<AdUnit>()有一些初始化数据,代码如下:

public class FakeUnitOfWork : IUnitOfWork
{
    private static List<AdUnit> _adUnits = new List<AdUnit>()
    {
        {
            new AdUnit
            {
                Id = "E1",
                Creatives = new Creative[]
                {
                    new Creative
                    {
                        ImageSrc = "http://static.cnblogs.com/images/logo_small.gif",
                        Url = "http://www.cnblogs.com/"
                    }
                }
            }
        }
    };

    public IQueryable<T> Set<T>() where T : class
    {
        if (typeof(T) == typeof(AdUnit))
        {
            return _adUnits.AsQueryable();
        }
        else
        {
            throw new NotImplementedException();
        }
    }
  
}
dudu的主页 dudu | 高人七级 | 园豆:40773
提问于:2015-08-27 10:23
< >
分享
最佳答案
1

return new List<AdUnit>().AsQueryable(); 改为 return new List<T>().AsQueryable();

 

那就这样写:

return _adUnits.AsQueryable().Cast<T>();
收获园豆:40
田园里的蟋蟀 | 菜鸟二级 |园豆:423 | 2015-08-27 10:33

 .Cast<T>() 是更简单的正解

dudu | 园豆:40773 (高人七级) | 2015-08-27 11:39

用dynamic也可以:

return new List<AdUnit>().AsQueryable() as dynamic;

 

dudu | 园豆:40773 (高人七级) | 2015-08-27 12:02

@dudu: 嗯,不过这种方式感觉怪怪的!

田园里的蟋蟀 | 园豆:423 (菜鸟二级) | 2015-08-27 12:22

@田园里的蟋蟀: 借助dynamic,可以这样实现:

FakeUnitOfWork:

public IQueryable<T> Set<T>() where T : class
{
    if(typeof(T) == typeof(AdUnit))
    {
        return AdUnitData.AdUnits;
    }
    else
    {
        throw new NotImplementedException();
    }
}

AdUnitData:

public class AdUnitData
{
    private static List<AdUnit> _adUnits = new List<AdUnit>();
 
    public static dynamic AdUnits
    {
        get
        {
            return _adUnits.AsQueryable();
        }
    }
}
dudu | 园豆:40773 (高人七级) | 2015-08-27 12:55

@dudu: 嗯,好设计!

不过用dynamic,我总感觉有些怪怪的,比如这家伙会让你忽略类型,如果把AdUnitData改成下面的代码:

public class AdUnitData
{
    private static List<AdUnitXXXXXX> _adUnits = new List<AdUnitXXXXXX>();
 
    public static dynamic AdUnits
    {
        get
        {
            return _adUnits.AsQueryable();
        }
    }
}

编译是没有什么问题,但运行就会抛出异常。

田园里的蟋蟀 | 园豆:423 (菜鸟二级) | 2015-08-27 13:42

@田园里的蟋蟀: 管它怪还是不怪,能有效地解决问题就行

dudu | 园豆:40773 (高人七级) | 2015-08-27 14:34

@dudu: 嗯,确实能解决问题!

我想表达的是:尽量不要设置方法或属性返回类型为dynamic,因为调用方根本不清楚到底是什么类型,如果原类型修改了,编译也完全看不出来错误,既然AdUnitData已经确定类型为AdUnit,那返回dynamic就有点多余了。

田园里的蟋蟀 | 园豆:423 (菜鸟二级) | 2015-08-27 17:00
其他回答(8)
0

同意楼上。

顾晓北 | 园豆:9520 (大侠五级) | 2015-08-27 10:33
0

仍旧放 T 不用AdUnit

泛型的具体参数类型不应该在方法里确定,是在外部的时候确认的,这样不报错才怪。。

FYeed | 园豆:9 (初学一级) | 2015-08-27 10:35
0

干嘛不在IUnitOfWork 扩展一个泛型出来,直接定义一个类型IUnitOfWork<T> 然后

FakeUnitOfWork : IUnitOfWork<AdUnit> 即可转换,

而如果在FakeUnitOfWork 转换,耦合度会大幅度增加不说,还要考虑所有的类型转换问题。

收获园豆:10
````` | 园豆:14268 (专家六级) | 2015-08-27 10:36

这个转换只是在Fake时需要

支持(0) 反对(0) dudu | 园豆:40773 (高人七级) | 2015-08-27 10:52
0

这样 return new List<T>().AsQueryable() 不能满足你的需求吗?

 

那就这样:

return (IQueryable<T>)new List<AdUnit>().AsQueryable();

收获园豆:40
Launcher | 园豆:45030 (高人七级) | 2015-08-27 10:38

经验证, (IQueryable<T>)new List<AdUnit>().AsQueryable(); 可行。

支持(0) 反对(0) dudu | 园豆:40773 (高人七级) | 2015-08-27 11:11

@dudu: 这样装箱真的好吗。。

支持(0) 反对(0) FYeed | 园豆:9 (初学一级) | 2015-08-27 11:44

@风晕: 本来就在箱子里

支持(0) 反对(0) dudu | 园豆:40773 (高人七级) | 2015-08-27 12:02

@dudu:没能理解..

支持(0) 反对(0) FYeed | 园豆:9 (初学一级) | 2015-08-27 12:06
0
  public interface IUnitOfWork : IDisposable
    {
        IQueryable<T> Set<T>() where T : class;
    }


    public class AdUnit
    {
    }


    public class BaseUnitOfWork<T> : IUnitOfWork
    {
        public IQueryable<T> Set<T>() where T : class
        {
            return new List<T>().AsQueryable();
        }

        public void Dispose()
        {
            throw new NotImplementedException();
        }
    }
    public class FakeUnitOfWork : BaseUnitOfWork<AdUnit>
    {
    }

我觉得你想要的效果应该是这样的

收获园豆:10
刘宏玺 | 园豆:14004 (专家六级) | 2015-08-27 10:58
            if (typeof(T) == typeof(AdUnit))
            {
                return (IQueryable<T>)_adUnits.AsQueryable();
            }
            else
            {
                throw new NotImplementedException();
            }

 

支持(0) 反对(0) 刘宏玺 | 园豆:14004 (专家六级) | 2015-08-27 11:09

BaseUnitOfWork只能这样定义:

public class BaseUnitOfWork<T> : IUnitOfWork
{
    public IQueryable<T1> Set<T1>() where T1 : class
    {
        throw new NotImplementedException();
    }
}

T1不能改为T

支持(0) 反对(0) dudu | 园豆:40773 (高人七级) | 2015-08-27 11:35
0

用1L的方法应该就妥了

CrazyJinn | 园豆:799 (小虾三级) | 2015-08-27 11:18
0

直接泛型实现就不需要去转了,在外面具体调用不好多了

稳稳的河 | 园豆:4183 (老鸟四级) | 2015-08-27 12:49
0

那里用T与你那个效果一样。为什么非要去直接写出来?但是返回值类型不匹配啊。

gw2010 | 园豆:1368 (小虾三级) | 2015-08-27 16:56
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册