Using random rnd inside a method causes multiple errors - c#

Having problem with the method down below. The current error I get is "it does not return any value" If I put the return num outside of the brackets, it does not exist in the current context.
If I remove the brackets all together I get:
Embedded statement cannot be a declaration or labeled statement
The name 'rnd' does not exist in the current context
I've done multiple methods before without this issue. What am I missing?
public static int generateNumber(int timesUserWantsToGuess)
{
for (int i = 0; i <= timesUserWantsToGuess; i++)
{
Random rnd = new Random();
int num = rnd.Next(1, 50);
return num;
}
}

First off you have a mismatch of semantics. You are returning a single int, but are executing and returning within a loop.
If you actually wanted to return a collection of ints, you could do something like this:
public static IEnumerable<int> generateNumber(int timesUserWantsToGuess)
{
//Optional, but good error checking
if (timesUserWantsToGuess <= 0)
throw new ArgumentException("Invalid guess count");
Random rnd = new Random();
for (int i = 0; i < timesUserWantsToGuess; i++)
{
int num = rnd.Next(1, 50);
yield return num;
}
}
Note the use of yield to return the next object in the sequence. You also shouldn't instantiate Random in a tight loop, since it will cause the same values to be generated (same seed). Also, using <= in the for conditional will yield one more iteration than the passed value, so I switched to <.
If you didn't want a loop at all, then just remove it and your problem will be solved. At the moment, the loop semantic and the rest of your code mismatch, which is causing you the problem.

The issue is that since the return statement exists inside a loop, the compiler assumes there's a chance it could execute without ever hitting the block, and thus won't return. This could logically happen if timesUserWantsToGuess is less than 0 - the loop never enters in this case. The reason this code compiles if the return statement is outside the block is that by being outside the block, the execution guarantees it'll hit the return (or throw an exception) at some point.
If you remove the brackets on a block in C# (if, for, foreach, lock, and while all do this to my knowledge) you are saying that the block contains only the next line (which is indented by convention) and nothing more. Since variable declaration on a one-line ("embedded") statement can't be used anywhere else syntactically, it doesn't make sense to do it, so the compiler forbids it altogether. Be noted it is also possible to declare a variable in a block without using it, as long as the block is bracketed.
Finally, there's a huge semantic mismatch in the logic of returning a single value during a loop like this: see #BradleyDotNET's answer on yield return statements

Related

Task.Run(), passing parameter to

Consider the following code:
attempt = 0;
for (int counter = 0; counter < 8; counter++)
{
if (attempt < totalitems)
{
Tasklist<output>.Add(Task.Run(() =>
{
return someasynctask(inputList[attempt]);
}));
}
else
{
break;
}
attempt++;
}
await Task.WhenAll(Tasklist).ConfigureAwait(false);
I want to have for example 8 concurrent tasks, each working on different inputs concurrently, and finally check the result, when all of them have finished.
Because I'm not awaiting for completion of Task.Run() attempt is increased before starting of tasks, and when the task is started, there may be items in the inputList that are not processed or processed twice or more instead (because of uncertainty in attempt value.
How to do that?
The problem lies within the use of a "lambda": when Task.Run(() => return someasynctask(inputList[attempt])); is reached during the execution, the variable attempt is captured, not its value (i.e. it is a "closure"). Consequently, when the lambda gets executed, the value of the variable at that specific moment will be used.
Just add a temporary copy of the variable before your lambda, and use that. E.g.
if (attempt < totalitems)
{
int localAttempt = attempt;
Tasklist<output>.Add(Task.Run(() =>
{
return someasynctask(inputList[localAttempt]);
}));
}
Thanks to #gobes for his answer:
Try this:
attempt = 0;
for (int counter = 0; counter < 8; counter++)
{
if (attempt < totalitems)
{
Tasklist<output>.Add(Task.Run(() =>
{
int tmpAttempt = attempt;
return someasynctask(inputList[tmpAttempt]);
}));
}
else
{
break;
}
attempt++;
}
await Task.WhenAll(Tasklist).ConfigureAwait(false);
Actually, what the compiler is doing is extracting your lambda into a method, located in an automagically generated class, which is referencing the attempt variable. This is the important point: the generated code only reference the variable from another class; it doesn't copy it. So every change to attempt is seen by the method.
What happens during the execution is roughly this:
enter the loop with attempt = 0
add a call of the lambda-like-method to your tasklist
increase attempt
repeat
After the loop, you have some method calls awaiting (no pun intended) to be executed, but each one is referencing THE SAME VARIABLE, therefore sharing its value - the last affected to it.
For more details, I really recommend reading C# in depth, or some book of the same kind - there are a lot of resources about closure in C# on the web :)

Stack Overflow in random number generator

For some reason, this code works fine when I don't use a seed in the Random class, but if I try to use DateTime.Now to get a more random number, I get a StackOverflowException! My class is really simple. Could someone tell me what I'm doing wrong here? See MakeUniqueFileName.
public class TempUtil
{
private int strcmp(string s1, string s2)
{
try
{
for (int i = 0; i < s1.Length; i++)
if (s1[i] != s2[i]) return 0;
return 1;
}
catch (IndexOutOfRangeException)
{
return 0;
}
}
private int Uniqueness(object randomObj)
{
switch (randomObj.ToString())
{
case "System.Object":
case "System.String":
return randomObj.ToString()[0];
case "System.Int32":
return int.Parse(randomObj.ToString());
case "System.Boolean":
return strcmp(randomObj.ToString(), "True");
default:
return Uniqueness(randomObj.ToString());
}
}
public string MakeUniqueFileName()
{
return "C:\\windows\\temp\\" + new Random(Uniqueness(DateTime.Now)).NextDouble() + ".tmp";
}
}
You're calling DateTime.Now.ToString(), which doesn't give you one of the strings you're checking for... so you're recursing, calling it with the same string... which still isn't one of the strings you're looking for.
You don't need to use Random to demonstrate the problem. This will do it very easily:
Uniqueness(""); // Tick, tick, tick... stack overflow
What did you expect it to be doing? It's entirely unclear what your code is meant to be doing, but I suggest you ditch the Uniqueness method completely. In fact, I suggest you get rid of the whole class, and use Path.GetTempFileName instead.
In short:
It should say
switch (randomObj.GetType().ToString())
instead of
switch (randomObj.ToString())
But even then this isn't very clever.
You are passing a DateTime instance to your Uniqueness method.
This falls through and calls itself with ToString - on a DateTime instance this will be a formatted DateTime string (such as "21/01/2011 13:13:01").
Since this string doesn't match any of your switch cases (again), the method calls itself again, but the result of calling ToString on a string is the same string.
You have caused an infinite call stack that results in the StackOverflowException.
There is no need to call Uniquness - when creating a Random instance, it will be based on the current time anyways.
I suggest reading Random numbers from the C# in depth website.
The parameter-less constructor of Random already uses the current time as seed value. It uses the time ticks, used internally to represent a DateTime.
A problem with this approach, however, is that the time clock ticks very slowly compared to the CPU clock frequency. If you create a new instance of Random each time you need a random value, it may be, that the clock did not tick between two calls, thus generating the same random number twice.
You can simply solve this problem by creating a single instance of Random.
public class TempUtil {
private static readonly Random random = new Random();
public string MakeUniqueFileName()
{
return #"C:\windows\temp\" + random.NextDouble() + ".tmp";
}
}
This will generate very good random numbers.
By the way
System.IO.Path.GetTempFileName()
automatically creates an empty temporary file with a unique name and returns the full path of that file.
Where to begin.
1. There is already a string compare. Use it. It has been debugged.
2. Your Unique function is illogical. The first two case items return a 'S' perhaps cast to an int. You have neglected the break on the first case.
Your third case is like this:
if (x =="System.Int32") return int.Parse("System.Int32");
That may return 32, or a parse error.
Your fourth case is like this:
if (x == "System.Boolean") return strcmp("System.Boolean", "True");
Your default case is called recursevly (sp) causing the stack overflow (see comment above)
In order fix this program, I recommend you read at least one good book on C#, then rethink your program, then write it. Perhaps Javascript would be a better fit.

Access to modified Closure

Will this fail ?
Resharper reports this as an instance of "Access to modified Closure"
Will the lambda be triggered for every value ? Is the iterator generating the complete list of all interval values in this before the line that changes first runs? Or is the line first = itvl;
running for reach iteration, and that changed value of first used for subsequent iterations ??
public HourInterval FirstInterval
{
get
{
var first = HourInterval.Make(DateTime.MaxValue);
foreach (var itvl in this.Where
(itvl => itvl < first))
first = itvl;
return first;
}
}
NOTE. HourInterval is a value-type struct that represents each one-hour-long calendar hour... and this is an IEnumerable collection of HourInterval objects
EDIT:
The above is what Resharper suggested to convert to a LINQ expression from the below foreach construction ...
public HourInterval FirstInterval
{
get
{
var first = HourInterval.Make(DateTime.MaxValue);
foreach (var itvl in this)
if(itvl < first)
first = itvl;
return first;
}
}
OK, this is a bit of a mess.
First off, it is a poor programming practice to use the same variable name in two slightly inconsistent ways in the same code. It's very confusing. Frankly, I would prefer this to be illegal; the reason that it is not illegal is a bit complicated; see http://blogs.msdn.com/b/ericlippert/archive/2009/11/05/simple-names-are-not-so-simple-part-two.aspx for details.
Let's get rid of that problem:
var first = HourInterval.Make(DateTime.MaxValue);
foreach (var itvl in this.Where(x => x < first))
first = itvl;
Now, the next question is: is Resharper correct to note that this is an access to a modified closure? Yes, Resharper is correct; you are modifying a closed-over variable of a lambda that will be called repeatedly. Resharper is noting that this is dangerous because Resharper does not know what "Where" does. For all Resharper knows, "Where" is caching every predicate it gets and is saving it up to execute later, in the mistaken belief that each predicate will do something different. In fact each predicate is the same, because each predicate is closed over the same variable, not closed over different variables.
Clearly no sensible implementation of "Where" will do that. But Resharper doesn't know that.
The next question is: is this a sensible thing to do? No. This is a terribly unidiomatic and confusing way to implement "Min", by modifying the closed-over variable of a predicate to "Where". If you want to write Min, just write Min:
static DateTime? Min(this IEnumerable<DateTime> seq)
{
DateTime? min = null;
foreach(DateTime current in seq)
{
if (min == null || current < min.Value)
min = current;
}
return min;
}
There, that returns the earliest date in the sequence, or null if the sequence is empty. No messing about with Where and predicates and mutated closures and all that nonsense: write the code to be straightforward and obviously correct.
Your code should work, but that's an unnecessarily complicated way to do it.
Try
this.Aggregate((min, next) => next < min ? next : min);

Is the condition in a for loop evaluated each iteration?

When you do stuff like:
for (int i = 0; i < collection.Count; ++i )
is collection.Count called on every iteration?
Would the result change if the Count property dynamically gets the count on call?
Yes Count will be evaluated on every single pass. The reason why is that it's possible for the collection to be modified during the execution of a loop. Given the loop structure the variable i should represent a valid index into the collection during an iteration. If the check was not done on every loop then this is not provably true. Example case
for ( int i = 0; i < collection.Count; i++ ) {
collection.Clear();
}
The one exception to this rule is looping over an array where the constraint is the Length.
for ( int i = 0; i < someArray.Length; i++ ) {
// Code
}
The CLR JIT will special case this type of loop, in certain circumstances, since the length of an array can't change. In those cases, bounds checking will only occur once.
Reference: http://blogs.msdn.com/brada/archive/2005/04/23/411321.aspx
Count would be evaluated on every pass. If you continued to add to the collection and the iterator never caught up, you would have an endless loop.
class Program
{
static void Main(string[] args)
{
List<int> intCollection = new List<int>();
for(int i=-1;i < intCollection.Count;i++)
{
intCollection.Add(i + 1);
}
}
}
This eventually will get an out of memory exception.
Yes count is checked at every call from the first iteration after the initialization of i to the last iteration where the check fails and the for loop is exited. You can modify the collections count if you want but realize you could end up in an endless loop.
Like the other answers here: Yes, in principal.
There is (at least) one noticeable exception, array.Length. In
for (int i = 0; i < a.Length; i++) a[i] = ...;
The Length property will only be evaluated once. This is a optimization that is hardwired into the compiler. There might be others like that (in the future) but only if it is guaranteed not to make a difference in observable behavior.
Side note, this is NOT checked for every interation in VB.
Unlike C#, VB caches the result of the collection.Count.
EDIT:
The literal VB version of the C# for loop is:
Dim i = 0
Do While i < collection.Count
'code goes here
i+=1
Loop

Invalid token 'while' in class, struct, or interface member declaration in very simple code

I am not sure what the problem is but I keep receiving this error when I try to use a while statement in my code.
Invalid token 'while' in class,
struct, or interface member
declaration
I want to use a while loop to have something continuously update while a statement is true.
The rest of my code is rather long but whenever I type in the syntax:
while(a<b)
{
//do whatever i want it to do here
}
It gives me that compiler error right off the bat. Not quite sure what the problem is. I am doing this in a C# windows application under the Form1.cs file with all the other event handlers (for buttons and such).
Thanks!
I was unaware that loops had to be placed within a method (fairly new to c#), but I tried it and no errors were returned. Thanks for your help everybody!
Previously, I just had the loop within the main class of the program.
Based on the error, it sounds like the compiler thinks this code is typed directly in the body of a class/struct/interface declaration. Statements while/if/for/etc ... must appear with in a method.
Try moving this code into a method to fix the problem. If it's in a method, you likely have a mismatched brace problem.
There's nothing wrong with the while, it's something above it that's the problem.
Check for mismatched braces and semicolons in a comment or something like that.
C# is not allowed to write code directly into the classes; it is allowed to write only data members and function members directly into the classes.
You can also get this if you have punctuation issues, I got it today when I missing a simple parentheses:
public static string GetPasswordForEnvironment)
should have been:
public static string GetPasswordForEnvironment()
But the error showed up on the first "if" statement later in the function.
C# does not provide writing the for, while or foreach kind of looping statement directly inside the class. These statements should be within a user defined method, main method or constructors.
class Array
{
public static void main(string[] args)
{
int[] n = new int[10]; /* n is an array of 10 integers */
/* initialize elements of array n */
for (int i = 0; i < 10; i++)
{
n[i] = i + 100;
}
/* output each array element's value */
foreach (int j in n)
{
int i = j - 100;
Console.WriteLine("Element[{0}] = {1}", i, j);
}
}
}

Categories

Resources