泛型方法的多重约束?

时间:2015-05-27 23:57:31

标签: c# generics ienumerable

我有以下四种重载方法Add:

    public IEnumerable<TrackInfo> Add(DataContext dataContext, IEnumerable<TrackInfo> tracks)
    {
        return tracks.Select(t => Add(dataContext, t));
    }

    public IEnumerable<TrackInfo> Add(DataContext dataContext, IEnumerable<string> files)
    {
        return files.Select(f => Add(dataContext, f));
    }

    public TrackInfo Add(DataContext dataContext, TrackInfo track)
    {
        dataContext.TrackInfos.InsertOnSubmit(track);
        Add(track);
        return track;
    }

    public TrackInfo Add(DataContext dataContext, string path)
    {
        return Add(dataContext, new TrackInfo(path));
    }

有没有办法让第一次和第二次重载成泛型函数?其他一些抽象机制也会有所帮助。

澄清我的意思(这不会编译!):

public IEnumerable<TrackInfo> Add<T>(DataContext dataContext, IEnumerable<T> items) where T : TrackInfo, string
{
    return items.Select(i => Add(dataContext, i));
}

首先,我不能使用字符串作为约束,因为它是密封的。其次,我认为我不能用这种方式指定多个约束。这有什么解决方案吗?

5 个答案:

答案 0 :(得分:2)

不是最好的答案,但我认为这可能是你想要的:

public IEnumerable<TrackInfo> Add<T>(DataContext dataContext, IEnumerable<T> tracks) where T : class
{
    if(typeof(T) == typeof(string)) 
    {
        return tracks.Select(t => Add(dataContext, new TrackInfo(t)));
    }
    else if(typeof(T) == typeof(TrackInfo)) 
    {
        return tracks.Select(t => Add(dataContext, t as TrackInfo));
    }
    else 
    {
        throw new ArgumentException("The type must be string or TrackInfo");
    }
}

public TrackInfo Add(DataContext dataContext, TrackInfo track)
{
    dataContext.TrackInfos.InsertOnSubmit(track);
    Add(track);
    return track;
}

// you may not need this
public TrackInfo Add(DataContext dataContext, string path)
{
    return Add(dataContext, new TrackInfo(path));
}

答案 1 :(得分:1)

您可以通过向跟踪信息类添加隐式强制转换来避免不断构建。

public static隐式运算符TrackInfo(string s)     {         返回新的TrackInfo;     }

来自IEnumerable&lt;字符串&gt;到IEnumerable&lt; TrackInfo&gt;将需要一个显式的转换扩展方法。我会留下它,就像你拥有它一样。

答案 2 :(得分:1)

您可以指定多个泛型类型约束;但是,当您这样做时,用于关闭通用的类型必须满足所有约束。这不可能与TrackInfo和字符串一起使用,因为它们都是&#34;具体的&#34;类型(类)之间可能没有继承层次结构(System.String类是密封的),因此没有类型可以从这两个类继承。

这段代码看起来很好。您可以通过调用&#34;主要重载&#34;来缩短调用堆栈的触摸时间。通过简单地从方法调用中的字符串构造TrackInfo(或者使用第二个Select来从每个字符串构造TrackInfo),从declare重载中获取单个TrackInfo)(<{1}})。

一个气味;通常,Select中使用的lambdas通常不会产生副作用;很明显,委托中使用的Add()方法除了返回&#34;投影&#34;之外还做了其他事情。输入元素,但对于样式I仍然更喜欢分离投影和元素添加的代码,即使它最终更冗长。

答案 3 :(得分:0)

由于此部分new TrackInfo(path),您无法完全通用。可以通过重写第二个方法来删除最后一个方法:

public IEnumerable<TrackInfo> Add(DataContext dataContext, IEnumerable<TrackInfo> tracks)
{
    return tracks.Select(t => Add(dataContext, t));
}

public IEnumerable<TrackInfo> Add(DataContext dataContext, IEnumerable<string> files)
{
    return Add(dataContext, files.Select(f => new TrackInfo(f));
}

public TrackInfo Add(DataContext dataContext, TrackInfo track)
{
    dataContext.TrackInfos.InsertOnSubmit(track);
    Add(track);
    return track;
}

答案 4 :(得分:0)

受到了Wery Nguyen的启发,并在我的课堂上进行了全面的通用重构。我最初发布的片段不是完整的问题,但我认为这可以解释我的意思。我应该发布整件事。

我想出了这个:

    #region Private methods

    static IEnumerable<TrackInfo> ProcessItems<T>(IEnumerable<T> items, Func<DataContext, IEnumerable<T>, IEnumerable<TrackInfo>> func)
    {
        using (var dataContext = new DataContext())
        {
            foreach (var item in func(dataContext, items))
            {
                yield return item;
            }

            dataContext.SubmitChanges();
        }
    }

    static IEnumerable<TrackInfo> ProcessItems<T>(DataContext dataContext, IEnumerable<T> items, Func<DataContext, T, TrackInfo> func)
    {
        return items.Select(t => func(dataContext, t));
    }

    TrackInfo ProcessItem<T>(DataContext dataContext, T item, Action<TrackInfo> action)
    {
        if (typeof(T) == typeof(string))
        {
            return ProcessItem(dataContext, this[item as string], action);
        }

        if (typeof(T) == typeof(TrackInfo))
        {
            var track = item as TrackInfo;
            action(track);
            return track;
        }

        throw new ArgumentException("The type must be string or TrackInfo");
    }

    #endregion


    #region Public methods

    public IEnumerable<TrackInfo> Add<T>(IEnumerable<T> items)
    {
        return ProcessItems(items, Add);
    }

    public IEnumerable<TrackInfo> Add<T>(DataContext dataContext, IEnumerable<T> items)
    {
        return ProcessItems(dataContext, items, Add);
    }

    public TrackInfo Add<T>(DataContext dataContext, T item)
    {
        return ProcessItem(dataContext, item, 
            i =>
            {
                dataContext.TrackInfos.InsertOnSubmit(i);
                Add(i);
            });
    }

    public IEnumerable<TrackInfo> Delete<T>(IEnumerable<T> items)
    {
        return ProcessItems(items, Delete);
    }

    public IEnumerable<TrackInfo> Delete<T>(DataContext dataContext, IEnumerable<T> items)
    {
        return ProcessItems(dataContext, items, Delete);
    }

    public TrackInfo Delete<T>(DataContext dataContext, T item)
    {
        return ProcessItem(dataContext, item,
            i =>
            {
                dataContext.TrackInfos.Attach(i);
                dataContext.TrackInfos.DeleteOnSubmit(i);
                Remove(i);
            });
    }

代码中没有冗余,但我不确定可读性是多么好。尽管如此,这仍然是有趣的学习经历。

只是解释一下。有两个通用ProcessItem和一个ProcessItem。每一个都是Add和Delete连续重载的基本方法。那些是:

  • 添加/删除单个项目,
  • 添加/删除多个项目,
  • 通过创建DataContext添加/删除多个项目。

每个单一操作都由Action定义,Action通过调用链传播。 删除:

dataContext.TrackInfos.Attach(i);
dataContext.TrackInfos.DeleteOnSubmit(i);
Remove(i);

添加:

dataContext.TrackInfos.InsertOnSubmit(i);
Add(i);