开发者

"Ternary" operator for different method signatures

I'm looking for an elegant way to choose a method signature (overloaded) and pass an argument based on a conditional. I have an importer that will either produce the most recent file to import, or take an explicit path for the data.

Currently my code looks like this:

if (string.IsNullOrEmpty(arguments.File))
{
    importer.StartImport();
}
else
{
    importer.StartImport(arguments.File);
}

I would like it to look like this (or conceptually si开发者_如何学Pythonmilar):

importer.StartImport(string.IsNullOrEmpty(arguments.File) ? Nothing : arguments.File);

The idea being that a different method signature would be called. A few stipulations:

1) I will not rely on 'null' to indicate an unspecified file (i.e. anything besides null itself).

2) I will not pass the arguments struct to the importer class; that violates the single responsibility principle.

One solution I'm aware of, is having only one StartImport() method that takes a single string, at which point that method resolves the conditional and chooses how to proceed. I'm currently choosing to avoid this solution because it only moves the if-statement from one method to another. I'm asking this question because:

1) I would like to reduce "8 lines" of code to 1.

2) I'm genuinely curious if C# is capable of something like this.


I would like to reduce "8 lines" of code to 1.

I think you're asking the wrong question. It's not how many lines of code you have, it's how clear, maintainable, and debuggable they are. From what you've described, importing from a default location and importing with a known file are semantically different - so I think you're correct in separating them as two different overloads. In fact, you may want to go further and actually name them differently to further clarify the difference.

I'm genuinely curious if C# is capable of something like this.

Sure, we can use all sorts of fancy language tricks to make this more compact ... but I don't think they make the code more clear. For instance:

// build a Action delegate based on the argument...
Action importAction = string.IsNullOrEmpty(arguments.File) 
                            ? () => importer.StartImport() 
                            : () => importer.StartImport(arguments.File)
importAction(); // invoke the delegate...

The code above uses a lambda + closure to create a Action delegate of the right type, which is then invoked. But this is hardly more clear ... it's also slightly less efficient, as it requires creating a delegate and then invoking the method through that delegate. In most cases the performance overhead is completely negligible. The real problem here is the use of the closure - it's very easy to misuse code with closures - and it's entirely possible to introduce bugs by using closures incorrectly.


You can't do this using 'strong-typed' C#. Overload resolution is performed at compile time: the compiler will work out the static (compile-time) type of the argument, and resolve based on that. If you want to use one of two different overloads, you have to perform two different calls.

In some cases, you can defer overload resolution until runtime using the dynamic type. However, this has a performance (and clarity!) cost, and won't work in your situation because you need to pass a different number of arguments in the two cases.


Yes, it is possible (using reflection). But I wouldn't recommend it at all because you end up with way more code than you had before. The "8-lines" which you have are quite simple and readable. The "one-liner" using reflection is not. But for completeness sake, here's a one-liner:

importer.GetType().GetMethod("StartImport", string.IsNullOrEmpty(arguments.File) ? new Type[0] : new Type[] { typeof(string) }).Invoke(importer, string.IsNullOrEmpty(arguments.File) ? new object[0] : new object[] { arguments.File) }));


Frankly, I think your code would be a lot more readable and modular if you combined the overloaded methods back into a single method and moved the conditional inside it instead of making the caller pre-validate the input.

importer.StartImport(arguments.File);

void StartImport(string File) {
  if (string.isNullOrEmpty(File)) {
      ...
  }
  else {
      ...
  }
}

Assuming you call the method from multiple places in your code you don't have the conditional OR ternary expression scattered around violating the DRY principle with this approach.


//one line 
if (string.IsNullOrEmpty(arguments.File)) {importer.StartImport();} else{importer.StartImport(arguments.File);}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜