Given this initial code
C#class Program
{
const string DEV_API_KEY = "Development Key", DEV_API_SECRET = "Development Secret";
const string PROD_API_KEY = "Production Key", PROD_API_SECRET = "Production Secret";
static string ApiKey(bool a_isDev) { return a_isDev ? DEV_API_KEY : PROD_API_KEY; }
static string ApiSecret(bool a_isDev) { return a_isDev ? DEV_API_SECRET : PROD_API_SECRET; }
static void GetOAuthToken(string a_hostname, bool a_isDev)
{
var request = WebRequest.Create(a_hostname + "/oauth/token");
request.Headers.Add("Authorization", "Basic " + Convert.ToBase64String($"{ApiKey(a_isDev)}:{ApiSecret(a_isDev)}".Select(ch => (byte)ch).ToArray()));
//...
}
}
Select the "Inline Method" or "Inline Method (and delete)" refactoring on the call to ApiKey in the second line of GetOAuthToken. That line is converted to:
C#request.Headers.Add("Authorization", "Basic " + Convert.ToBase64String($"{a_isDev ? DEV_API_KEY : PROD_API_KEY}:{ApiSecret(a_isDev)}".Select(ch => (byte)ch).ToArray()));
This is not valid C# code; the ternary operator needs to be enclosed in parentheses when inside a string interpolation.
The same incorrect inlining appears when I trigger it from the method definition, or on the ApiSecret method.
UPDATE
A thematically similar issue but functionally much worse issue can occur when the caller uses the return value in an expression. In this case, the code may compile correctly, but have different behavior than the original. Given this initial code:
C#class Program
{
static int Foo(bool a_useFive) { return a_useFive ? 5 : 10; }
static void Main()
{
System.Console.WriteLine(Foo(true) + " items");
System.Console.WriteLine(Foo(true) * 2);
}
}
Use the "Inline Method (and delete)" code action on Foo, and the body of Main will get changed to:
C#System.Console.WriteLine(true ? 5 : 10 + " items");
System.Console.WriteLine(true ? 5 : 10 * 2);
The first line no longer compiles, and the second line now outputs "5" instead of "10".
The same issue can be seen with any other expression where the precedence change from "function call" to "ternary" causes the operator in the caller to bind to the third argument to the ternary instead of to the result of the ternary.
Hi Dale,
Thank you for informing us about this problem and providing us with such a detailed explanation.
I was able reproduce this.
Please give us some time. We will prepare corrections and inform you about that.
There are also problems if the parameter in the caller is an suitably-crafted expression. Add
static bool GetSomething(); static bool GetSomethingElse();
and call:
bool a; System.Console.WriteLine(Foo(a ? GetSomething() : GetSomethingElse()));
The inlining operation yields this, which again doesn't compile:
System.Console.WriteLine(a ? GetSomething() : GetSomethingElse() ? 5 : 10);
Hi Dale,
Thank you for providing us with the additional test scenario.
We'll examine it and prepare the correction.