Referring to an object defined by a variable - c#

I have a problem, which I couldn't solve recently.
I have this code
foreach (Hashtable i in (ArrayList)inv["database"])
{
if (i != null)
{
if (i["type"].ToString() == "1")
{
if (i["dataValue"].ToString() != "0")
{
inv{nn}.Image = Program.Properties.Resources._i["type"].ToString()+"-"+i["dataValue"].ToString();
}
else
{
inv{nn}.Image = Program.Properties.Resources._i["type"].ToString()
}
}
}
nn++;
}
I have 36 controls(it's a class i have in my project, so not "vanilla" one), and each one of them is a single "picturebox". I have 505 images in the following syntax:
If the data value is 0, then it's {typeID}.png, if the data value is above 0, then {typeID}-{dataValue}.png
So for example if it's the first loop, i["type"].ToString() = 1 and i["dataValue"].ToString() = 3, the Image of inv0 changes to Program.Proporties.Resources._1-3
When the second loop comes the image of inv1 changes etc. ... till inv35
Is such thing possible? I tried
InterpolationBox x = Form1.FindControl("inv"+nn)
and I seem not to have such thing as FindControl (FrameWork 4) with System.Web.UI used.
I tried
InterpolationBox x = this.Controls.Find("inv" + nn, false);
And I got Unable to cast object of type 'System.Windows.Forms.Control[]' to type 'Program.InterpolatedBox'. And anyway, if I'd get rid of that error, would I be able to change that actual picturebox, not just the copied 'x' one?

InterpolationBox x = this.Controls.Find("inv" + nn, false);
Is where your problem lies.
The error you got is telling:
Unable to cast object of type 'System.Windows.Forms.Control[]' to type
'Program.InterpolatedBox'.
You can see it's saying it can't cast an array of System.Windows.Forms.Control to a Program.InterpolationBox which is sensible given InterpolationBox is a single control.
The Find method returns an array of controls, not just one, so you need to then look in the array and pull out the one you're after (even if it's just the first one).
As for your question about whether changing x would change the control you got given using Controls.Find() the answer is YES.
Objects in .NET are by reference, which means x isn't an InterpolationBox it's a reference to an InterpolationBox in memory.
You could even do:
var a = x;
var b = a;
var c = b;
c.DoSomething();
The code you call against c will operate against the same object, they're all just references pointing to the same thing.

Related

Array Questions

Recently had a quiz in my C# class and got some things wrong. I think I have the answers but I want to make sure I am right.
First one:
Explain the result
int[] myArray = {5,10,15,20,25};
DoWork(myArray);
void DoWork(int[] theArray)
{
for (int c = 0; c < theArray.Length; c++)
{
theArray[c] = 1;
}
theArray = null;
}
For this one, I only got half of it right. I said that the loop would set the value for each element in the array to 1. So my question is, what happens when you set the array to null?
Second one:
Explain the result
int[] myArray = {5,10,15,20,25};
DoWork(myArray[1]);
void DoWork(int theItem)
{
theItem = -1;
}
This one I got completely wrong. The correction was that myArray[1] = 10 still. Is this because it is not being passed by reference? This just confused me a lot because I ran a little test program on the first one (without the null part) and all the values were set to 1 in the array but I was not passing by reference.
Q: what happens when you set the array to null?
A: "theArray" (inside the routine) is set to null. But "myArray" (outside of the routine) is UNCHANGED. The reason is that "myArray" is an object reference, which is passed by value into DoWork().
Q: Is this because it is not being passed by reference?
A: Yes, exactly. From the link above:
https://msdn.microsoft.com/en-us/library/9t0za5es.aspx
Any changes to the parameter that take place inside the method have no
affect on the original data stored in the argument variable.
These links explain further:
C# Parameter Passing, Ref and Out
C# - Passing Parameters by Reference

c# wrong number of indices inside [] expected 2

I am doing PrimSparse algorithm and it says I need two indices in my for loop
private Node z;
adj = new int[V + 1, V + 1];
Node t;
for (t = adj[v]; t != z; t = t.next)
{
if (t.wgt < dist[u])
{
//puts the distance between the two vertex as the current data
dist[v] = t.wgt;
parent[u] = v;
//making sure when the vertex is == 0 then insert the vertex
if (hPos[u] == 0)
{
h.insert(t.vert);
}
else
{
//or otherwise just move up the position of the vertex
h.siftUp(hPos[t.vert]);
}
}
}
the very first line is giving me the CS0022 error, absolutely no idea why, when i put in a second it says "cannot convert int to Graph.node"
any ideas?
The answer is relatively straightforward:
CS0022 is "Wrong number of indices inside []"
adj is declared as a 2D array of ints, so you will need to supply two indices when you dereference it, not one. This is just how arrays work.
You seem to have correctly diagnosed that issue - so onto the 2nd:
Dereferencing an adj gives you an int. You're then trying to assign an int to t, which has a type of "node" There's no conversion defined between "int" and "node", thus the 2nd error message. You can read it as straight English, "Cannot convert an int to a node" - but you have to make one more logical step. Why is a conversion from int to node necessary? Because you're trying to do assignment. Maybe a more verbose error message would be "Cannot assign an int to a variable of type Node because there is no conversion specified"
Some Lurking issues:
Comparing T and Z should be no problem. They're both Nodes. You may need to provide your own equality operator though. By default, != is going to do a reference compare. You may or may not want to compare references.
Finally, assuming the type of t.next is also a node, the last part of your for loop should be OK too.

Convert one type into another

I have a problem with convertion types. My mainForm keeps variable in integer type. Also my form has propertyGrid where I realized property for field (like combobox) with Image & Text. And now I don't understand well how can I convert one type into another. First I need to convert data from int to myProp and then vice versa.
Here setup propertyGrid:
public dashPatternList DashPattern
{
get { return dashPattern; }
set { dashPattern = value; }
}
Here I tried to realize my problem with additional methods:
private dashPatternList dashIN(int dash)
{
dashPatternList ds = dashPatternList.pic0;
if (dash == 1) ds = dashPatternList.pic1;
if (dash == 2) ds = dashPatternList.pic2;
return ds;
}
private int dashOUT(dashPatternList dash)
{
int i = 0;
if (dash == dashPatternList.pic1) i = 1;
if (dash == dashPatternList.pic2) i = 2;
return i;
}
And call it:
pData.DashPattern = dashIN(dashPattern);
dashPattern = dashOUT(pData.DashPattern);
This method works, but maybe you suggest me more easy way.
You could keep the pictures in an array, so instead of dashIN(dash) you'd write dashIN[dash] (and you don't need to write the dashIN function). You just need to initialize it once with something like this:
DashPattern[] dashIN = new DashPattern[] {
dashPatternList.pic0, dashPatternList.pic1, dashPatternList.pic2 };
For the reverse, something like Array.IndexOf(dashIN,mypic) should work.
This way you replace code with data, which tends to be a good thing as it's usually easier to manage. For example now you only have to change one line if you want to change the list of dash patterns, instead of having to change the code in two functions earlier. Plus now it's impossible to make an error that would cause dashOUT(dashIN(dash))!=dash (as would happen if there's a wrong number in the code).

Add TextBox.Text to a list using a for loop

I am trying to take the values in the textboxes, named sequentially from 0-9, and add that to a List using a for loop. I am having problems with the syntax or something.
here is what I have now.
for (int i = 0; i <= amt.Count(); i++)
{
amt[i] = int.Parse(amtBox[i].Text);
}
The error is that amtBox doesnt exist in the current context.
My problem is within the loop where i have amtBox[i].Text. I have tried this several ways and VS always throws an error. I have tried "amtBox" + i and that compiles but then causes an error when I try to do something with it and says "data is of wrong type".
I am new to C# and come from PHP so maybe that is why I think this approach will work. PHP doesnt care about data types where C# really does. I have done this exact thing in PHP many times without any issue.
Any suggestions on another way to do this are appreciated as I am probably coming at this all wrong.
Thanks
One solution would be to declare an array and assign amtBox'es to the individual indexes in the array and then you can iterate on that array.
var amtBoxes = new TextBox[] { amtBox0, amtBox1, .... };
for (int i = 0; i <= amt.Count(); i++)
{
amt[i] = int.Parse(amtBoxes[i].Text);
}
If you end up needing to iterate on your TextBox controls in other places I would consider making the array an instance member of your object.
I suppose that your textbox are named "amtBox" + a number.
(The Name property is "amtBox1" as an example)
In this case you could use
Control[] t = Controls.Find("amtBox" + i, false);
for a code like this
for (int i = 0; i <= amt.Count(); i++)
{
Control[] t = Controls.Find("amtBox" + i, false);
if(t != null && t.Length > 0)
{
amt[i] = int.Parse(t[0].Text);
}
}
My understanding is that you have text boxes named amtBox1, amtBox2, etc., and what you are trying to do is sequence through them. As you point out, this is very easy in PHP. It is possible to do what you're suggesting using reflection, but that is expensive and, in any event, there's probably a better way to do what you're looking for.
You could put all of your amount boxes into an array, and then what you have would work:
var amtBoxes = new[] {
amtBox1,
amtBox2,
amtBox3
}

Should I assign parameter values to local variables first instead of using them directly?

Is there any reason to assign parameter values to local variables inside a method in order to use those values without changing them? I.e. like the following:
private void MyMethod(string path)
{
string myPath = path;
StreamReader mystream = new StreamReader(myPath);
...
}
Or can I always put it like this (and the code above is redundant and just not clean):
private void MyMethod(string path)
{
StreamReader mystream = new StreamReader(path);
...
}
I know it works both ways, but I'd like to be sure there isn't anything I missed in my understanding.
The only time you need to do this (assign locally) is if you are in a foreach loop or using Linq. Otherwise you can run into issues with modified closures.
Here is a snippet from an MSDN blog (All content below is from the link).
http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx
But I'm getting ahead of myself. What's the output of this fragment?
var values = new List<int>() { 100, 110, 120 };
var funcs = new List<Func<int>>();
foreach(var v in values)
funcs.Add( ()=>v );
foreach(var f in funcs)
Console.WriteLine(f());
Most people expect it to be 100 / 110 / 120. It is in fact 120 / 120 / 120. Why?
Because ()=>v means "return the current value of variable v", not "return the value v was back when the delegate was created". Closures close over variables, not over values. And when the methods run, clearly the last value that was assigned to v was 120, so it still has that value.
This is very confusing. The correct way to write the code is:
foreach(var v in values)
{
var v2 = v;
funcs.Add( ()=>v2 );
}
Now what happens? Every time we re-start the loop body, we logically create a fresh new variable v2. Each closure is closed over a different v2, which is only assigned to once, so it always keeps the correct value.
Basically, the problem arises because we specify that the foreach loop is a syntactic sugar for
{
IEnumerator<int> e = ((IEnumerable<int>)values).GetEnumerator();
try
{
int m; // OUTSIDE THE ACTUAL LOOP
while(e.MoveNext())
{
m = (int)(int)e.Current;
funcs.Add(()=>m);
}
}
finally
{
if (e != null) ((IDisposable)e).Dispose();
}
}
If we specified that the expansion was
try
{
while(e.MoveNext())
{
int m; // INSIDE
m = (int)(int)e.Current;
funcs.Add(()=>m);
}
then the code would behave as expected.
It's exactly the same thing, the only difference is that in the first case you make a copy of the reference (which is destroyed anyway when the method gets out of scope, which happens when the execution ends).
For better readability, stick with the second case.
I prefer the second option. It makes no sense to create a new variable with the parameter. Also, from a reading perspective, it makes more sense to create a stream from a path (the one you received) instead of instantiating a "myPath" variable.

Categories