1

Interesting problem here. I have a class that deals with System.Data elements that was original written to use concrete classes as parameters. It was recently updated to use interfaces instead so that we had greater compatibility with other databases.

My problem comes from the following method, and its updated counterpart:

Old:

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

New:

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

The old version results in a DbTransaction object being spit out that we can send to other methods in our projects, while the newer one gives back an IDbTransaction. I named the updated version NewTransaction so that the older projects out there will not be broken should the end up sitting alongside the newer library (DLL Hell and whatnot).

I can make rename NewTransact to Transact and it will compile just fine, but I don't see a way to call one over the other. My current solution will work just fine, but eventually NewTransact will no longer be "new". At that time, removing the old method and changing the name on the new one will just cause the problem to show up again.

My question is: Is there a way to pick which specific method is called if they're both the same name?

I'm guessing there isn't, but I figured this would make a good question.

EDIT: The key component to this question is maintaining backwards compatibility. Any answer that would require updating old software to continue to function is irrelevant. If I wanted to go back and update other projects I would simply do that in the first place rather than creating these overloads.

3 Answers 3

1

Here is a sample that works and how you can force which will be called:

      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; }
   }

As someone else stated, the compiler will determine which function is being called. But in my example, Foo is also an IFoo so I assume this is the crux of the issue.

Output:

Transact(Func<Foo, bool> funcBlock)
isFoo: True

Transact(Func<IFoo, bool> funcBlock)
isIFoo: True

Update per comment
To summarize and put this in scope of the question, use one of the following to determine which function your code should call:

((DbTransaction transaction) => ...)

... or

((IDbTransaction transaction) => ...)
Sign up to request clarification or add additional context in comments.

4 Comments

If I'm understanding this right, you can sum up this answer as "Use ((DbTransaction transaction) => ...) or ((IDbTransaction transaction) => ...).
That would be the summary, yes :)
Is there a specific term for this action? It's basically explicit type declaration in a situation where you wouldn't even use var.
I'm not sure... probably explicit declaration is appropriate.
0

Method which compiler picks based on parameter types will be called.

For example:

    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);

3 Comments

While this is a good solution to the general problem of delegates as parameters, it does not apply to my situation. I cannot make such a drastic change to the method that would result in re-coding other projects.
Your new code should pass Func with IDbTransaction interface parameter and other existing projects will continue passing Func with DbTransaction class parameter. Thus your new code will use new method, and existing code will use old, existing method. So you can make a choice which method to use only in new code if you don't want to re-code other projects. If you want to delete existing method so that only one with IDbTransaction exists, then you will have to re-code other projects since they won't compile.
I guess I should have shown how we call the code currently. We something along the lines of dao.Transact(transaction => dao.GetNewCommand("query", transaction)); The Transact blocks tend to be quite large.
0

Maybe rename both to Transactand then in your new code try to get your method like below:

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

And then call it:

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

This should give you a way to call specified method, while the old code will still be using Transact(Func<DbTransaction, bool> functionBlock)as the implementation closest to provided argument.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.