C#通用循环返回列表w / type

时间:2018-02-13 18:04:45

标签: c# generics inheritance

我注意到我有一些重复的代码。如何以更通用的方式完成这项工作?

班级结构是

class BaseObject
class MyBlock1 : BaseObject
class MyBlock2 : BaseObject
class MyBlock3 : BaseObject

循环是:

private List<MyBlock1> GetVideosSection(IEnumerable<ContentAreaItem> items)
    {
        List<MyBlock1> blocks = null;
        if (items != null)
        {
            blocks = new List<MyBlock1>();
            foreach (var reference in items)
            {
                var block = _repo.Get<MyBlock1>(reference.ContentLink);
                blocks.Add(block);
            }
        }
        return blocks;
    }

BaseObject的每个实现重复循环。

items参数是一个&#39;列表&#39;引用&#39; ID&#39;用于查找并返回适当对象。

我的尝试是按照

的方式进行的
private List<T> GetBlocks<T>(IEnumerable<ContentAreaItem> items) where T : BlockData, new ()
    {
        List<BaseObject> blocks = null;
        if (items != null)
        {
            T typeDeclaredInMethod = T from GetBlocks<T>;
            blocks = new List<  typeDeclaredInMethod >();
            foreach (var reference in items)
            {
                var block = _repo.Get< typeDeclaredInMethod >(reference.ContentLink);
                blocks.Add(block);
            }
        }
        return blocks;
    }

我意识到上面的代码无效,但有说明我的意图。 我是C#的新手,不知道泛型方法是如何工作的,所以不确定这是否可行。 如何编写此循环以适用于BaseObject的所有实现?

我正在寻找的例子:

List<MyBlock1> block1List = GetBlocks<MyBlock1>( items );
List<MyBlock2> block2List = GetBlocks<MyBlock2>( items );
List<MyBlock3> block3List = GetBlocks<MyBlock3>( items );

List<BaseObject> block1List = GetBlocks<MyBlock1>( items );

2 个答案:

答案 0 :(得分:2)

您的代码已关闭但您无需获取T,您已经拥有它:

private List<T> GetVideosSection<T>(IEnumerable<ContentAreaItem> items)
    {
        List<T> blocks = null;
        if (items != null)
        {
            blocks = new List<T>();
            foreach (var reference in items)
            {
                var block = _repo.Get<T>(reference.ContentLink);
                blocks.Add(block);
            }
        }
        return blocks;
    }

我会写如下:

private List<T> GetVideosSection<T>(IEnumerable<ContentAreaItem> items) => items?.Select(reference => _repo.Get<T>(reference.ContentLink)).ToList();

答案 1 :(得分:1)

让我们首先说明如何进行概括。我们从:

开始
private List<MyBlock1> GetVideosSection(IEnumerable<ContentAreaItem> items)
{
    List<MyBlock1> blocks = null;
    if (items != null)
    {
        blocks = new List<MyBlock1>();
        foreach (var reference in items)
        {
            var block = _repo.Get<MyBlock1>(reference.ContentLink);
            blocks.Add(block);
        }
    }
    return blocks;
}

我们希望对此进行参数化以消除MyBlock1,因此我们只需声明一个新的类型参数 T并搜索并替换MyBlock1

private List<T> GetVideosSection<T>(IEnumerable<ContentAreaItem> items)
{
    List<T> blocks = null;
    if (items != null)
    {
        blocks = new List<T>();
        foreach (var reference in items)
        {
            var block = _repo.Get<T>(reference.ContentLink);
            blocks.Add(block);
        }
    }
    return blocks;
}

我们完成了吗?我们完成了原始任务,但总是借此机会询问您是否可以做得更好。这种方法可以改进吗?当然。例如,我们可以注意到“接受并返回null”的合同是可怕的。这是容易出错和错误的。空序列便宜。不要将null视为空序列。既不接受也不归还。

private List<T> GetVideosSection<T>(IEnumerable<ContentAreaItem> items)
{
    if (items == null) throw new ArgumentNullException("items");
    List<T> blocks = new List<T>();
    foreach (var reference in items)
    {
        var block = _repo.Get<T>(reference.ContentLink);
        blocks.Add(block);
    }
    return blocks;
}

好多了。我们完了吗?不。我们现在看到我们的循环只是一个选择:

private List<T> GetVideosSection<T>(IEnumerable<ContentAreaItem> items)
{
    if (items == null) throw new ArgumentNullException("items");
    var query = from reference in items 
                select _repo.Get<T>(reference.ContentLink);
    return query.ToList();
}

或者如果您愿意

private List<T> GetVideosSection<T>(IEnumerable<ContentAreaItem> items)
{
    if (items == null) throw new ArgumentNullException("items");
    return items.Select(reference => _repo.Get<T>(reference.ContentLink)
                .ToList();
}

我们还能做得更好吗?在C#7中,我们可以在条件表达式的主体中放置一个表达式的方法:

private List<T> GetVideosSection<T>(IEnumerable<ContentAreaItem> items) => 
  items == null ? 
    throw new ArgumentNullException("items") :
    items.Select(reference => _repo.Get<T>(reference.ContentLink)
         .ToList();

现在我们可能会问自己,我们真的需要ToList吗?如果调用者想要使用Where进一步过滤结果,该怎么办?将其列为清单还为时过早:

private IEnumerable<T> GetVideosSection<T>(IEnumerable<ContentAreaItem> items) => 
  items == null ? 
    throw new ArgumentNullException("items") :
    items.Select(reference => _repo.Get<T>(reference.ContentLink));

或者如果您愿意:

private IEnumerable<T> GetVideosSection<T>(IEnumerable<ContentAreaItem> items) => 
  items == null ? 
    throw new ArgumentNullException("items") :
    from reference in items select _repo.Get<T>(reference.ContentLink);

如果来电者想要一个列表,那么他们可以说GetVideosSection<Whatever>(items).ToList(),嘿,他们有一个列表。

看看与原始代码相比简洁易懂。原始代码说“这个方法最重要的是我用这个循环填写的列表”。此版本的代码强调了含义:我从与每个项目相关联的仓库中提取内容