3

在重写抽象方法并尝试调用我当前正在重写的基类方法时,我看到了一个非常奇怪的问题。

//In Dll "A"
namespace Rhino.Etl.Core.Operations
{
    using System;
    using System.Collections;
    using System.Collections.Generic;

    public class Row {}

    public interface IOperation
    {
        IEnumerable<Row> Execute(IEnumerable<Row> rows);
    }

    public abstract class AbstractOperation : IOperation
    {
        public abstract IEnumerable<Row> Execute(IEnumerable<Row> rows);
    }

    public abstract class AbstractDatabaseOperation : AbstractOperation
    {
    }

    public abstract class SqlBulkInsertOperation : AbstractDatabaseOperation
    {
        public override IEnumerable<Row> Execute(IEnumerable<Row> rows)
        {
            Console.WriteLine("SqlBulkInsertOperation");
            return rows;
        }
    }
}

//In console app "B"
namespace MyStuff
{
    using System;
    using System.Collections;
    using System.Collections.Generic;

    class Program
    {
        static void Main(string[] args)
        {
            ActualEtlOperation e = new ActualEtlOperation();
            e.Execute(new Row[0]);

            Console.ReadLine();
        }
    }

    public abstract class SqlBulkInsertWithTruncateOperation : SqlBulkInsertOperation
    {
        public override IEnumerable<Row> Execute(IEnumerable<Row> rows)
        {
            Console.WriteLine("Truncate");
            base.Execute(rows);
            return rows;
        }
    }

    public class ActualEtlOperation : SqlBulkInsertWithTruncateOperation
    {

    }
}

基本上,SqlBulkInsertOperation 没有做我需要做的事情,所以我需要在通过覆盖它调用 Execute(rows) 之前和之后做一些工作。但是 SqlBulkInsertOperation.Execute(Rows) 中的代码没有被执行。

在 Visual Studio 的调试器中运行此代码时,调试器不会执行该代码。当我将鼠标悬停在 Visual Studio 编辑器中的“base”上时,它知道基类的类型是 SqlBulkInsertOperation。

我错过了什么?

4

5 回答 5

10

EDIT: I've found the problem... and ironically, my "psychic debugging" comment to Eric wasn't so far off, given this blog post. Here's a short but complete program which will demonstrate what's going on...

using System;
using System.Collections.Generic;

public class Row {}

public abstract class BaseDatabaseOperation
{
    public abstract IEnumerable<Row> Execute(IEnumerable<Row> rows);
}

public abstract class SqlBulkInsertOperation : BaseDatabaseOperation
{
    public override IEnumerable<Row> Execute(IEnumerable<Row> rows)
    {
        Console.WriteLine("In SqlBulkInsertOperation.Execute");
        foreach (var row in rows)
        {
            yield return row;
        }
    }
}

public class MyOverride : SqlBulkInsertOperation
{
    public override IEnumerable<Row> Execute(IEnumerable<Row> rows)
    {
        Console.WriteLine("In MyOverride.Execute");
        return base.Execute(rows);
    }
}

class Test
{
    static void Main()
    {
        BaseDatabaseOperation x = new MyOverride();
        x.Execute(new Row[0]);
    }
}

That will print "In MyOverride.Execute" but it *won't" print "In SqlBulkInsertOperation.Execute"... because that method is implemented with an iterator block.

Of course, you can demonstrate this much more simply:

using System;
using System.Collections.Generic;

class Test
{
    static IEnumerable<string> Foo()
    {
        Console.WriteLine("I won't get printed");
        yield break;
    }

    static void Main()
    {
        Foo();
    }
}

Nothing is using the return value of the method - it's being passed back to Main, but nothing ever calls GetEnumerator() on it, and then MoveNext() on the result... so the body of the method is never executed.

See my article on iterator blocks, part two of Eric's blog post, or download chapter 6 from the home page of the first edition of C# in Depth (chapter 6 covers iterator blocks, and is free) for more details on this.

于 2010-09-29T19:59:51.673 回答
6

Here's the code I ran:

using System;
using System.Collections.Generic;
public class Row {}
public abstract class BaseDatabaseOperation 
{ 
    public abstract IEnumerable<Row> Execute(IEnumerable<Row> rows); 
} 

public abstract class SqlBulkInsertOperation : BaseDatabaseOperation 
{ 
    public override IEnumerable<Row> Execute(IEnumerable<Row> rows) 
    { 
        Console.WriteLine("base");
        return null;
    } 
} 

public class MyOverride : SqlBulkInsertOperation 
{ 
    public override IEnumerable<Row> Execute(IEnumerable<Row> rows) 
    { 
        Console.WriteLine("override");
        base.Execute(rows);
        return null;
    } 
} 

static class P 
{
    static void Main()
    {
        var m = new MyOverride();
        m.Execute(null);
    }
}

It runs fine; it produces "override" and "base".

Tell you what: modify the program I've posted here so that it produces the problem you are experiencing, and we'll analyze that. It is very difficult to analyze a problem when you cannot actually see the real code that is having problems.

于 2010-09-29T20:02:45.173 回答
5

对我来说很好。你的代码需要一点清理——你确定你正在编译和执行这段代码,而不是调试你的代码的旧版本吗?

class Program
{
    public class Row { }

    public abstract class BaseDatabaseOperation
    {
        public abstract IEnumerable<Row> Execute(IEnumerable<Row> rows);
    }

    public abstract class SqlBulkInsertOperation : BaseDatabaseOperation
    {
        public override IEnumerable<Row> Execute(IEnumerable<Row> rows)
        {
            Console.WriteLine("done");
            return rows;
        }
    }

    public class MyOverride : SqlBulkInsertOperation
    {
        public override IEnumerable<Row> Execute(IEnumerable<Row> rows)
        {
            return base.Execute(rows);
        }
    }

    static void Main(string[] args)
    {
        var x = new MyOverride();
        x.Execute(null);
    }
}
于 2010-09-29T20:05:22.963 回答
3

Your example does not compile, some returns are missing, but the following modified example

public class Row
{
}

public abstract class BaseDatabaseOperation
{
    public abstract IEnumerable<Row> Execute(IEnumerable<Row> rows);
}

public abstract class SqlBulkInsertOperation : BaseDatabaseOperation
{
    public override IEnumerable<Row> Execute(IEnumerable<Row> rows)
    {
        Console.WriteLine("does useful work");
        return new Row[0];
    }
}

public class MyOverride : SqlBulkInsertOperation
{
    public override IEnumerable<Row> Execute(IEnumerable<Row> rows)
{

        Console.WriteLine("do own work here");

    //This does not execute code in base class!
        base.Execute(rows);

        return new Row[0];
}
}

Called like this:

MyOverride mo = new MyOverride();
mo.Execute(new Row[0]);

generates the output

do own work here
does useful work

You must have done something else not included in your example.

于 2010-09-29T20:02:35.313 回答
3

这些都是同时编译的吗?您可能正在遭受断开的中间基类http://blogs.msdn.com/b/ericlippert/archive/2010/03/29/putting-a-base-in-the-middle.aspx

于 2010-09-29T20:03:56.297 回答