Why is a return required when Environment.Exit() is used, but not for a thrown exception? - c#

I'm trying to better understand the compiler for C#. It insists that all code paths must return a value, and I think that's pretty fair.
It also recognizes that if an exception is thrown in a path where a value would need to be returned, that there is no point in returning something there. This also makes sense.
My question is: why wouldn't this also apply for exiting the program in a more graceful manner? e.g Environment.Exit()
-Examples-
This will compile:
private string TestMethod(int x, int y)
{
if (x == y)
{
return "this is a string";
}
throw new Exception();
// No point in a return after this, it could never be reached.
}
This will NOT compile:
private string TestMethod(int x, int y)
{
if (x == y)
{
return "this is a string";
}
Environment.Exit(1);
// This will not compile.
// "Not all code paths return a value"
// But, the code would never make it to the return here.
}

Environment.Exit is nothing but a method as far as the compiler is concerned.
It enforces that the TestMethod either return a value or throw an exception. Calling a method that might terminate the application or do something completely different is not a valid way to "return" from a method.

Related

Getting "not all code paths return a value" for code that will never not return a value

The method I write doesn't return a value though I write every possible conditions to return a value.
What I understand is whenever the compiler sees the return keyword it'll stop, execute the program and return the value next to the return keyword. Even if it is in the loop it'll break the loop and return the value but it the compiler shows the "not all code paths return a value" error. And there's an else conditions for the other possible conditions and how comes the error show up.
So, how the return keyword actually work?
I am so confused.
using System;
public static class PhoneNumber
{
public static (bool IsNewYork, bool IsFake, string LocalNumber) Analyze(string phoneNumber)
{
bool flag = true;
string[] numSub = phoneNumber.Split("-");
while(flag)
{
if(numSub[0] == "212")
{
if(numSub[1] == "555")
{
return (true, true, numSub[2]);
}
else{return (true, false, numSub[2]);}
} // end of if condition
else{
if(numSub[1] == "555")
{
return (false, true, numSub[2]);
}
else{return (false, false, numSub[2]);}
} // end of the else condition
} // end of the loop
}
not all code paths return a value
The compiler is not "smart" enough to know that you will enter the while loop. So it sees the code path that doesn't enter the while loop as a possible code path without a return.
As-written the code structure doesn't make much sense, so it should probably be restructured to make the compiler happy, and be easier to read and maintain. You can also just a thrown exception after the while to get rid of the compilation error.

C# lambda null runtime binding

I'm running into an odd scenario that doesn't happen on my PC, but does for a coworkers.
I have this piece of code:
LoaderHelpers.SetStringValue<blah>(this, "x", $"x response in Header",
() => jsonData.x.response[0].value, false);
The problem is that sometimes, "jsonData.x" is null and, for my coworker a 'cannot bind to null at runtime exception' is thrown, but not for me there isn't. I have code to handle the null scenario, but it's like his code never gets to that point and fails at the call level.
jsonData is of type dynamic.
The method code that handles the null scenario:
public static void SetStringValue<T>(IValidate data, string propertyName,
string valuePath, Func<string> value, bool required)
{
if (data.GetType().GetProperty(propertyName) != null)
{
try
{
if (string.IsNullOrEmpty(value()))
{
if (required)
data.DataValidationErrors.Add($"{valuePath} can't be empty");
data.GetType().GetProperty(propertyName).SetValue(data, null);
}
else
{
data.GetType().GetProperty(propertyName).SetValue(data, value());
}
}
catch
{
//property doesn't exist
if (required)
data.DataValidationErrors.Add($"{valuePath} doesn't exist");
data.GetType().GetProperty(propertyName).SetValue(data, null);
}
}
else
{
throw new NullReferenceException($"In {data.GetType()} => SetStringValue. " +
$"Passed property {propertyName}, but property doesn't exist.");
}
}
Again. Works perfect for me, but not for him. I'm completely lost. Maybe I don't understand how the lamba/function parameters work 100%, but I thought it only got evaluated when value() is invoked.
I should also mention that when I debug this code, I can step into the Nuget package and when he hits the same line, he can't. This maybe a useful hint.
If jsonData (or jsonData.x) is null (as it seems to be at this point) it will crash and give you that error every time you call the method value().
You need to check why jsonData.x is null. Maybe it´s a race condition caused by another thread setting this value to null, maybe it´s because a bad jsonData initialization... Can´t say since that code is not here.
There are so many things wrong with your code, i can't resist.
First of all, instead of copy/pasting the same stuff over and over, you might want to use a variable:
var property = data.GetType().GetProperty(propertyName);
Second, you pass a Func<string> and execute it multiple times, why is it even a function then? Yet again, better only evaluate it once and use a variable...
var unwrapped = value();
That would solve the issue, that Roberto Vázquez' answer adresses.
Then you are misusing NullReferenceException, instead rather use a ArgumentException
Next issue, that valuePath is only used in the exception message, that is a poor design to my beliefs.
The generic T parameter isnt even used, so get rid of it.
Last but not least, that catch-block doing the exact thing that could possibily throw the exception again, i cant see any reason why you would do this.
Finnaly this whole thing becomes a little more clear but its still a mess.
public static void SetStringValue(IValidate data, string propertyName,
string valuePath, Func<string> value, bool required)
{
if(data == null)
throw new ArgumentNullException(nameof(data));
var property = data.GetType().GetProperty(propertyName);
if(property == null)
throw new ArgumentException($"In {data.GetType()} => SetStringValue. " +
$"Passed property {propertyName}, but property doesn't exist.");
var unwrapped = value();
try
{
if (string.IsNullOrEmpty(unwrapped))
{
if (required)
data.DataValidationErrors.Add($"{valuePath} can't be empty");
unwrapped = null; // this might be unecessary.
}
property.SetValue(data, unwrapped);
}
catch(Exception e)
{
// This is probably a bad idea.
property.SetValue(data, null);
if (required)
data.DataValidationErrors.Add(Atleast put a better message here. e.Message ...);
}
}

Can a [pure] function throw an exception?

Is it OK for a function that can throw an exception to have the [pure] attribute?
According to
https://msdn.microsoft.com/en-us/library/system.diagnostics.contracts.pureattribute(v=vs.110).aspx
PureAttribute attribute
Indicates that a type or method is pure, that is, it does not make any
visible state changes.
So it's quite possible to throw an exception from such a method e.g.
// factorial is a pure function: no state will be changed,
// just a computation
[Pure]
public static BigInteger Factorial(BigInteger value) {
// We can't return infinity with BigInteger and that's why have to throw the exception
if (value < 0)
throw new ArgumentOutOfRangeException("value", "value must be non-negative");
...
}
And what if I call this pure method as
BigInteger result = Factorial(1000000000);
one of the possible outcomes is OutOfMemory exception thrown
You can throw an exception, you are not making any visible state changes. Here example from Reference source.
[Pure]
private void VerifyWritable() {
if (isReadOnly) {
throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ReadOnly"));
}
Contract.EndContractBlock();
}
I agree with Dmitry.
According to documentation from msdn:
All methods that are called within a contract must be pure; that is, they must not update any preexisting state. A pure method is allowed to modify objects that have been created after entry into the pure method.
Throwing an exception is allowed and will not necessarily be considered as a changing the object state.

Why are exceptions always accepted as a return type (when thrown)?

Why are exceptions always accepted as a return type (thrown)?
Valid example 1:
public string foo()
{
return "Hello World!";
}
Invalid example (obviously):
public string foo()
{
return 8080;
}
Valid example 2:
public string foo()
{
throw new NotImplementedException("Hello Stackoveflow!");
}
I can see that my "Invalid Example" is 'throwing' and not 'returning' the Exception, however, my method does never return the type defined by the method. How come my compiler allows this to compile?
Exceptions are not return types, exceptions indicate an error in the method and that it cannot continue.
Your valid example 2 does not return anything, the compiler knows that it will always throw an exception, so it does not need to worry about returning.
If you had:
public string foo(int something)
{
if(something > 10){
throw new NotImplementedException("Hello Stackoveflow!");
}
}
it would complain, because you are not returning a value all the time.
Also, from your example, if you had: string val = something() in your code, val would never get set, because an exception is not a return value.
However, it is valid code, your function can either return a value based on its signature or throw an exception. If anything, you might expect a warning by the compiler. I'm not sure about C#, but in java if you have code that is determined to be unreachable you will get warnings, such as:
public string foo(int something)
{
throw new NotImplementedException("Hello Stackoveflow!");
return "OK";
}
This code would give you a warning, because it is impossible for this method to reach the return statement (but it is still valid code, at least if it were Java).
You can read about exceptions here: MSDN
Exceptions give you information about ocurred error. You can easly handle and throw them.

Unhandled Exception in List Sort

So, I have a list containing a custom class, MyClass
MyClass has properties, which can be null (but aren't meant to be).
When this class is sorted, using a custom sorter, where the sorter accesses this null property and throws an exception, the exception is considered unhandled, even though there is a try-catch block around the sort method.
Now for some reason the exception still gets written to the console, which is what the exception handler is doing.
I have a real application with this same issue, causing my unit tests to fail, even though the exception is handled correctly and I cannot explain this.
So I have attached some sample code to explain myself better, run this from VS.
Updated Code
Results:
System.InvalidOperationException
Failed to compare two elements in the array.
Done!
So it seems to be handling my custom exception, and throwing its own?
using System;
using System.Collections.Generic;
using System.Data;
namespace TestSortException
{
class Program
{
static void Main()
{
try
{
var list = new List<MyClass>
{
new MyClass("1"),
new MyClass(null),
new MyClass("fdsfsdf")
};
list.Sort(new MyClassSorter());
}
catch(Exception e)
{
Console.WriteLine(e.GetType());
Console.WriteLine(e.Message);
}
Console.WriteLine("Done!");
Console.ReadLine();
}
}
class MyClassSorter : IComparer<MyClass>
{
public int Compare(MyClass x, MyClass y)
{
// try
// {
if (x.MyString == y.MyString)
return 0;
// Unhandled??? Exception here
if (x.MyString.Length > y.MyString.Length)
return 1;
return -1;
// }
// catch (Exception)
// {
// return -1;
// }
}
}
class MyClass
{
private string _myString;
public string MyString
{
get
{
if (_myString == null) throw new DataException("MyString is Null");
return _myString;
}
}
public MyClass(string myString)
{
_myString = myString;
}
}
}
There's a try/catch block round the Sort method, yes - and that catch block catches the exception. In other words, Sort throws an exception and your catch block catches it. It doesn't propagate out beyond Main - so "Done!" is printed.
This is exactly what I'd expect. In what way is it "unhandled" in your experience? Were you expecting Sort not to throw the exception? It needs to do something to indicate the failure to compare two elements, and this seems to be the most appropriate course of action.
In what way are your unit tests failing? Are you deliberately giving them invalid data? How do you want your comparison code to react to invalid data? If it should ignore it (and return a comparison based on another property), then you should actively check the property rather than letting an exception propagate. In most cases I'd rather allow the exception if this indicates that there's a bug earlier on though.
EDIT: Based on your other comments, it sounds like you're doing the appropriate thing, letting the exception bubble up - but it's not clear in what way you're seeing the exception not be handled.
If you're running in the debugger, it may be breaking on the exception being thrown, but that doesn't mean it won't be handled. Try either changing your exception settings or running without the debugger.
EDIT: Yes, Sort will catch the exception and throw an InvalidOperationException instead - but you can use the InnerException property of that exception to get hold of the original one. It's unfortunate that the documentation doesn't specify this :(
For example, when it checks that string "1" isn't equal to null. But it wants then to compare lengths of "1" string and null => which is impossible.
I assume you work with .Net Framework 4.0. The new thing there is that a NullRefenrenceException can not be caught any more (similar to OutOfMemory exception).

Categories

Resources