将产线提取为普通法

本文关键字:普通法 提取 | 更新日期: 2023-09-27 17:48:58

我们在代码中使用自动生成的代码(带有Repository模式的SubSonic3),并且有许多这样的行。

public IEnumerable<MyModels.StatusLookup> GetAll()
    {
        var results = Database.Current.pStatusLookupLoadAll()
            .ExecuteTypedList<MyModels.StatusLookup>();
        if (results.IsNull()) yield break;
        foreach (var m in results)
        {
            ..Common logic lines...
            ..Common logic lines...
            yield return m;
        }
    }

我想做的是将生成线重构为一个通用方法。但由于yield的工作方式,我不知道我是否可以。

然后,当我们有自定义代码调用数据库外的存储库自动生成的代码,然后我可以调用加载的模型对象上的这个常用方法。

public IEnumerable<Books> GetByFancy(int anInteger)
{
    DB db = Database.Current;
    var r = from b in db.Books
            join a in db.Authors on b.AuthorId equals a.AuthorId
            where a.AuthorId == anInteger
            select b;
    if (r.IsNull()) yield break;
    foreach (var m in r)
    {
        m.AcceptChanges();
        yield return m;
    }
}

所以上面的示例中有公共重复行,我想在其中进行一个公共方法调用来删除公共重复代码行。


这是我得到的例外。

System.InvalidCastException : Unable to cast object of type '<AcceptChangesAndYield>d__6' to type 'System.Collections.Generic.IEnumerable`1[MyModels.StatusLookup]'.
public IEnumerable<MyModels.StatusLookup> GetAll()
{
    var results = Database.Current.pStatusLookupLoadAll()
        .ExecuteTypedList<MyModels.StatusLookup>();
    return (IEnumerable<MyModels.StatusLookup>)results.AcceptChangesAndYield();
}

下面是我尝试过的扩展方法

public static class BaseModelExtensions
{
    public static IEnumerable<MyModels.BaseModel> AcceptChanges(this IEnumerable<MyModels.BaseModel> obj)
    {
        if (obj.IsNull()) yield break;
        foreach (var m in obj)
        {
            m.AcceptChanges();
            yield return m;
        }
    }
    public static IEnumerable<MyModels.Interfaces.ILookup> AcceptChangesAndYield(this IEnumerable<MyModels.Interfaces.ILookup> obj)
    {
        if (obj.IsNull()) yield break;
        foreach (var m in obj)
        {
            yield return m;
        }
    }
}

将产线提取为普通法

Update:您的问题与yield关键字无关。它与类型方差有关。

您的AcceptChangesAndYield方法返回实现IEnumerable<MyModels.Interfaces.ILookup>类型的对象(实际上它是编译器生成的类型,但这并不重要)。在您的方法调用中,您试图将向下转换这到IEnumerable<MyModels.StatusLookup>,这是更具体的

IEnumerable<T>接口是协变,这将允许您将上转换为不太特定的类型;例如,你可以从List<string>转换为IEnumerable<object>(无论如何在。net 4.0中)。编译器为提供AcceptChangesAndYield方法的返回值而生成的类型只实现IEnumerable<MyModels.Interfaces.ILookup>,因此您可以将结果强制转换为IEnumerable<object>(例如),但是不能将强制转换为IEnumerable<MyModels.StatusLookup>

幸运的是,解决方案非常简单。重新定义你的AcceptChangesAndYield方法,如下所示:

// Note: We are using a generic type constraint on T.
public static IEnumerable<T> AcceptChangesAndYield<T>(this IEnumerable<T> obj)
    where T : MyModels.Interfaces.ILookup
{
    if (obj.IsNull()) yield break;
    foreach (var m in obj)
    {
        // Did you mean to put m.AcceptChanges() here?
        yield return m;
    }
}

这将反过来允许您的GetAll方法实现如下:

public IEnumerable<MyModels.StatusLookup> GetAll()
{
    var results = Database.Current.pStatusLookupLoadAll()
        .ExecuteTypedList<MyModels.StatusLookup>();
    // Note: no need for a cast, as the return value is now
    // already strongly typed as IEnumerable<MyModels.StatusLookup>.
    return results.AcceptChangesAndYield();
}

原始答案:看起来你只是想要这个?

IEnumerable<T> EnumerateResults<T>(IEnumerable<T> results)
{
    if (results.IsNull()) yield break;
    foreach (T result in results)
    {
        // ..Common logic lines...
        yield return result;
    }
}

然后在你想要删除重复的代码中,你只需要:

// Specific stuff
var results = BlahBlahBlah();
// Common stuff
return EnumerateResults(results);

对吧?还是我误解了你的问题?