问题
For an Azure Functions project I am using C# 8 with enabled nullable reference types. Since with AzureFunctions AppSettings are exposed via environment variables, which can be a string or null (i.e. they return string?
) I tried to encapsulate the logic of getting environment variables - and throwing an error if they're not set - into a separate method GetEnvVariable
.
As you can see, GetEnvVariable
is implemented in three short lines. If the environment variable is not set (i.e it is null
) it throws a custom exception, else the string value is returned.
The following line does not show a problem of "possible de-reference of null" because the compiler know, that at this line env
must be string
(not string?
) because the exception would have fired already and this line could not have been reached.
var length = env.Contains('$');
Still, when hovering over the variable env
with the mouse the type is displayed as string?
Does this make sense? If so, why? env
is assigned the output of the GetEnvVariable
method, which return string
.
Does throwing an exception implicitly return null
? Please help me understand, your input is really appreciated. Cheers!
Here's my code
namespace delme_azf
{
public static class kv
{
[FunctionName("kv")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequest req,
ILogger log)
{
try
{
var envName = "lolomat";
var env = GetEnvVariable(envName);
var length = env.Contains('$');
return new OkObjectResult($"success {env}");
}
catch (System.Exception)
{
throw;
}
}
public static string GetEnvVariable(string name)
{
var env = Environment.GetEnvironmentVariable(name);
if (env == null) throw new EnvironmentVariableNotSetException(nameof(name));
return env;
}
public class EnvironmentVariableNotSetException : Exception
{
public EnvironmentVariableNotSetException() {}
public EnvironmentVariableNotSetException(string envName) : base($"Mandatory environment variable '{envName}' is not set.") {}
}
}
}
Also here's my full *.csproj showing that nullable reference types are enabled globally.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<LangVersion>8.0</LangVersion>
<AzureFunctionsVersion>v3</AzureFunctionsVersion>
<RootNamespace>delme_azf</RootNamespace>
</PropertyGroup>
<PropertyGroup>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Azure.Functions.Extensions" Version="1.0.0" />
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="3.0.3" />
<PackageReference Include="Oracle.ManagedDataAccess.Core" Version="2.19.70" />
</ItemGroup>
<ItemGroup>
<None Update="host.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="local.settings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</None>
</ItemGroup>
</Project>
回答1:
Throwing an exception does not make your method return null or any other value.
When you declare a var
of a reference type, the compiler will always infer a nullable type for it. The reason for this is that the compiler has full knowledge of whether or not the variable could contain null at any point in the method--so if you wanted to do something like the following, the compiler can understand what you're doing and stay out of your way:
var x = "initial value";
Console.WriteLine(x.ToString()); // ok
if (someCondition)
{
// there's no warning on this assignment, because
// `var x` was inferred to have a `string?` type in the beginning.
x = null;
}
CarryOnWith(x); // ok, assuming the method accepts a possibly null string
Visual Studio will show you the null state the compiler has inferred for your variable in Quick Info (by hovering over the variable):
See also the notes from the C# design team on the decision to make var
always infer a nullable reference type.
Below is some more context on what the language is doing:
Nullability analysis tracks both the declared type of a variable and the flow state of a variable. Consider an example like the following:
string? s = null;
if (s != null)
{
// if we somehow got here, s was not null.
Console.WriteLine(s.ToString());
}
else
{
// warning: dereference of a possibly null reference
Console.WriteLine(s.ToString());
}
s = "hello";
// since we know a not-null was assigned, we now know this is safe.
Console.WriteLine(s.ToString());
// additionally the flow state becomes not-null if
// a null value would have definitely caused an exception earlier on:
s = null;
// warning
Console.WriteLine(s.ToString());
// no warning, because the previous line would have already thrown an exception
Console.WriteLine(s.ToString());
In the above example, s
is of type string?
, but the compiler knows at each point whether or not it could contain null
-- so it's OK to access members on it or pass it off to methods that require a string
argument.
回答2:
the compiler know, that at this line
env
must bestring
(notstring?
) because the exception would have fired already and this line could not have been reached.
No, it doesn't. The compiler does not do in-depth analysis to know that your function cannot return a null
value.
If you've enabled nullable reference support either within the .csproj
file or via a #nullable enable
directive, then using the string
return type (as opposed to string?
) for GetEnvVariable
informs the compiler that the reference cannot be set be null. Without that support turned on, the compiler assumes that the string van be null.
来源:https://stackoverflow.com/questions/61898776/function-with-return-type-of-string-returns-a-nullable-string-ie-string