【问题标题】:Overloading method's delegate parameter with an interface version使用接口版本重载方法的委托参数
【发布时间】:2013-11-19 21:34:18
【问题描述】:

这里有一个有趣的问题。我有一个处理 System.Data 元素的类,这些元素最初是为了使用具体类作为参数而编写的。它最近更新为使用接口,以便我们与其他数据库具有更好的兼容性。

我的问题来自以下方法,以及它的更新对应:

旧:

public bool Transact(Func<DbTransaction, bool> functionBlock)
{
    ... Stuff ...
}

新:

public bool NewTransact(Func<IDbTransaction, bool> functionBlock)
{
    ... Stuff ...
}

旧版本导致DbTransaction 对象被吐出,我们可以将其发送到项目中的其他方法,而新版本则返回IDbTransaction。我将更新版本命名为 NewTransaction,这样如果最终与较新的库(DLL Hell 等)并排放置,旧项目就不会被破坏。

我可以将 NewTransact 重命名为 Transact 并且它会编译得很好,但我看不到一种方法可以调用另一个。我当前的解决方案可以正常工作,但最终 NewTransact 将不再是“新的”。到时候把旧的方法去掉,换个新的名字,只会让问题再次出现。

我的问题是:如果它们都同名,有没有办法选择调用哪个特定方法?

我猜没有,但我认为这是一个很好的问题。

编辑:这个问题的关键部分是保持向后兼容性。任何需要更新旧软件才能继续运行的答案都是无关紧要的。如果我想返回并更新其他项目,我会一开始就这样做,而不是创建这些重载。

【问题讨论】:

    标签: c# interface delegates backwards-compatibility


    【解决方案1】:

    这是一个有效的示例以及如何强制调用:

          private static readonly Program p = new Program();
    
          private Foo foo;
    
          static void Main(string[] args) {
             p.foo = new Foo();
             p.foo.IsFoo = true;
             p.foo.IsIFoo = true;
    
             // uses Transact(Func<Foo, bool> funcBlock)
             var isFoo = p.Transact(f => f.IsFoo == true);
             Console.WriteLine("isFoo: {0}", isFoo);
    
             //  this is ambiguous
             //var isIFoo = p.Transact(f=> (f as IFoo).IsIFoo == true);
    
             // this works
             var isIFoo = p.Transact((IFoo f) => f.IsIFoo == true);
             Console.WriteLine("isIFoo: {0}", isIFoo);
    
    
             Console.ReadLine();
          }
    
          bool Transact(Func<Foo, bool> funcBlock) {
             Console.WriteLine("Transact(Func<Foo, bool> funcBlock)");
    
             return (funcBlock(p.foo));
          }
    
          bool Transact(Func<IFoo, bool> funcBlock) {
             Console.WriteLine("Transact(Func<IFoo, bool> funcBlock)");
    
             return (funcBlock(p.foo));
          }
       }
    
       class Foo : IFoo {
    
          public bool IsFoo {
             get;
             set;
          }
    
          public bool IsIFoo {
             get;
             set;
          }
    
       }
    
       interface IFoo {
    
          bool IsIFoo { get; set; }
       }
    

    正如其他人所说,编译器将确定调用哪个函数。但在我的示例中,Foo 也是 IFoo,所以我认为这是问题的症结所在。

    输出:

    交易(Func&lt;Foo, bool&gt; funcBlock)
    isFoo: 是的

    交易(Func&lt;IFoo, bool&gt; funcBlock)
    isIFoo: 是的

    更新 每条评论
    总结一下并将其置于问题的范围内,请使用以下方法之一来确定您的代码应调用哪个函数:

    ((DbTransaction transaction) => ...)
    

    ... 或

    ((IDbTransaction transaction) => ...)
    

    【讨论】:

    • 如果我的理解正确,您可以将这个答案总结为“使用((DbTransaction transaction) =&gt; ...)((IDbTransaction transaction) =&gt; ...)
    • 这就是总结,是的:)
    • 此操作是否有特定术语?在您甚至不会使用 var 的情况下,它基本上是显式类型声明。
    • 我不确定...可能明确声明是否合适。
    【解决方案2】:

    将调用编译器根据参数类型选择的方法。

    例如:

        Func<DbTransaction, bool> f1 = dbt => false;
        Func<IDbTransaction, bool> f2 = idbt => false;
    
        // Possible from .NET 4.0 since type parameter T of Func<in T, out TResult> is contravariant.
        var f3 = (Func<DbTransaction, bool>)f2;
    
        // Calls Transact with Func<DbTransaction, bool> parameter.
        Transact(f1);
    
        // Calls Transact with Func<IDbTransaction, bool> parameter.
        Transact(f2);
    
        // Calls Transact with Func<DbTransaction, bool> parameter.
        Transact(f3);
    

    【讨论】:

    • 虽然这是解决委托作为参数的一般问题的好方法,但不适用于我的情况。我不能对可能导致重新编码其他项目的方法做出如此大的改变。
    • 您的新代码应该通过带有 IDbTransaction 接口参数的 Func,而其他现有项目将继续使用带有 DbTransaction 类参数的 Func。因此,您的新代码将使用新方法,而现有代码将使用旧的现有方法。因此,如果您不想重新编码其他项目,您可以选择仅在新代码中使用哪种方法。如果您想删除现有方法以便只存在一个带有 IDbTransaction 的方法,那么您将不得不重新编码其他项目,因为它们不会编译。
    • 我想我应该展示一下我们当前如何调用代码。我们类似于dao.Transact(transaction =&gt; dao.GetNewCommand("query", transaction)); Transact 块往往很大。
    【解决方案3】:

    也许将两者都重命名为Transact然后在您的新代码中尝试获取您的方法,如下所示:

    MethodInfo transact = YourType.GetMethod("Transact",new Type[] {typeof(Func<IDbTransaction, bool>)});
    

    然后调用它:

    bool returnVal = transact.Invoke(callingObject, new object[]{yourArgumentHere});
    

    应该为您提供一种调用指定方法的方法,而旧代码仍将使用Transact(Func&lt;DbTransaction, bool&gt; functionBlock)作为最接近提供的参数的实现。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多