Cannot compare object from Arraylist to the actual object - c#

I'm trying to select a random user control within an array list. I get the index of the array but it tells me it cannot simply convert int to UserControl. Anyone knows what I did wrong?
ArrayList notiList = new ArrayList();
int count = 0;
int i;
public MainPage()
{
this.InitializeComponent();
foreach (NotiObject noti in itemsPanel.Children.Where(c => c is NotiObject))
{
notiList.Add(noti);
System.Diagnostics.Debug.WriteLine(noti);
}
i = new Random().Next(0, notiList.Count);
}
void sendNotification()
{
NotiObject randomNoti = notiList.IndexOf(i);
}

As Dai has hinted, ArrayList is a particularly old thing, from back in the days when .net was relatively new and didn't have the incredibly useful feature known as generics.
The manual page for ArrayList says this (my emphasis):
Important
We don't recommend that you use the ArrayList class for new development.
Instead, we recommend that you use the generic List class.
Even the manufacturer is saying "don't use this product"
The big problem with ArrayList is that because it wants to be able to store anything, it holds its contents in an object array
This means you can put two completely unrelated things in next to each other, you have to inspect the type of them if you do, and you always have to cast to turn the object back into what you want
notiList.Add(123); //whoops, that's not a NotiObject
foreach(var o in notiList)
var n = (NotiObject)notiList[0]; //whoops, that's a crash
}
So, working with it is pretty wearisome, particularly the part where you have to cast all the time.. This gets boring very quickly:
object o = "hello";
object p = "world";
object q = (string)o + (string)p;
object r = ((string)q).Substring(3).IndexOf((stribg)p);
r = (int)r + ((int)r)/2;
Storing everything in an object can be done, but look at what a mess it is. You'd have to start putting the type name into the variable name just to help remember that r was an int, and q was a string - Hungarian notation's another relic of the past.
When you put things in an ArrayList, this is what you're doing; storing them in object
So generics were invented and List was invented. A list that can be custom made to store a single type of objects like a string, int or NotiObject
var nums = new List<int>();
nums.Add(123); //works
var notiList = new List<NotiObject>();
notiList.Add(123); //compiler refuses this one
Now I've said all that, it's possible to answer your question. This code doesn't make sense:
NotiObject randomNoti = notiList.IndexOf(i);
i is an integer. IndexOf is a method that finds the numeric index of an item in the list. If the list was "a","b","c" and you asked for IndexOf("b") the result is 1 because b is at the second index, and indexing starts from 0.
IndexOf is not "get me the object at index blahblah", it's "tell me the index of this object blahblah"
The code doesn't make sense because you've passed an integer in and the list stores NotiObject. IndexOf will never find an integer in a list of NotiObject. This was the first mistake. You were allowed to make it because ArrayList stores everything as objects so you're allowed to pass an integer into IndexOf even if there are no integers in the list
IndexOf returns an integer. You cannot assign an integer to a variable of type NotiObject. This is the thing the compiler is complaining about
Even if you form the code correctly, you still have to cast:
NotiObject randomNoti = (NotiObject)notiList[i];
It's all very wearisome and if you persist with ArrayList probably not the last mistake you'll make with it either
If you used a List<NotiObject> you wouldn't have been allowed to pass an integer to IndexOf; the compiler would have stopped you which would hopefully then have made you assess IndexOf in the docs, and see that it's for finding the int index from the object, not the object at int index
You'd write code like:
List<NotiObject> notiList = new List<NotiList>();
...
NotiObject randomNoti = notiList[i];
without the cast. If you want to read more into why there is no cast, check out some introductory articles to generics. In a nutshell generics (any time you see something like <T> or <TBlahBlah>) allow you to specify something like a template code skeleton that the compiler uses to create code for you; code that substitutes the for the kind of object you want to work with. There isn't any casting any more because the compiler will write a whole List class that dedicatedly only works with NotiObjects

Related

What IEnumerable<IEnumerable<>> means?

Some methods in c# that i have seen , requires IEnumerable<IEnumerable<"some class">>. When method need IEnumerable<"some class"> as a parameter , i can pass a one-dimensional array or list . I thought by the same logic i can pass two-dimensional array as IEnumerable<IEnumerable<"some class">> , but my compiler says it is not the same. What data types i can pass to such methods, for example?
For example IEnumerable<IEnumerable<int>> is an enumeration of an enumeration of integers.
https://www.codingame.com/playgrounds/213/using-c-linq---a-practical-overview/ienumerablet
https://learn.microsoft.com/dotnet/api/system.collections.generic.ienumerable-1
It means that each element of the root list is a list of int.
If we write:
IEnumerable<IEnumerable<int>> listOfLists = GetItems();
We can parse items like that:
foreach ( var list in listOfLists )
{
Console.WriteLine("Values");
foreach ( var value in list )
Console.WriteLine(" " + value);
}
In fact if we declare:
var items = new List<List<int>>();
This is a IEnumerable<IEnumerable<int>> here.
It is like an array of arrays of int:
var items = int[][];
Here it is not a multidimentionnal array but a jagged array:
https://learn.microsoft.com/dotnet/csharp/programming-guide/arrays/jagged-arrays
https://www.tutorialsteacher.com/csharp/csharp-jagged-array
https://www.c-sharpcorner.com/UploadFile/puranindia/jagged-arrays-in-C-Sharp-net
IEnumerable is just an interface, which is implemented by a lot classes/types, two of which the List and Array (you can find the list here).
So, as as example, when the compiler tells you must pass a IEnumerable<"some class"> parameter it can be, for instance a List (depending if the "some class" refers to a string... if it refers to a class Car created by you then you must pass a List...) or string[] (which is an array of strings, which also implements this interface).
If you have to pass a IEnumerable<IEnumerable<"some class">> that means you have to have to layers of objects which implement the IEnumerable interface. You can have things like:
List<List>
string[][]
And others...
Long story short, it’s an interface that allows you to do a foreach loop on certain objects.

C# Value Type Lists

I'm a bit confused. Structs are more or less value types that get constructed on the stack and therefore have a straightforward lifetime.
When building a list with a struct, you cannot modify them directly because the returned value is a copy, and won't actually modify the item stored in the list.
My confusion comes here: Why can I not directly change a struct item in a list, but I can directly access and modify the base value types (int, float, etc...)?
This works:
List<int> foobar1 = new List<int>();
foobar1.Add(1);
foobar1[0] = 2;
This Doesn't:
public struct foo
{
public int bar;
}
...
List<foo> foobar2 = new List<foo>();
foobar2.Add(new foo());
foobar2[0].bar = 2;
The two are fundamentally different, and not just because someone decided that it is, let me explain.
The first piece of code replaces wholesale the int value in the 0th element position in the list. It doesn't matter which int value is there, afterwards the list contains the int value 2 in the 0th position.
The second piece of code, however, is attempting to replace parts of the struct. Yes, I know, the struct only has one field but the compiler makes no such distinction. You're effectively modifying a copy of the struct retrieved from the list. This is not allowed.
So the first piece of code just stuffs a new value type into the list, the second piece of code tries to modify the value type from the list, which is a copy.
So, can you change the second piece of code to be like the first, ie. replace the element in the list completely?
Sure:
var temp = foobar[0];
temp.bar = 2;
foobar2[0] = temp; // no longer modifies the copy, but replaces the element
Basically, this right here:
foobar2[0].bar = 2;
^ ^
| |
is the problem.

Assigning SqlParameter.Value without knowing type or using mixed type array

I've got a data layer I'm working on that calls into a database three times with three different stored procedures. I initially created three different functions to retrieve three dimensionalities of results. The first returns a single value, the second an entire row, and the third a table. They also take in different parameters. The first takes two varchars, the second two ints, and the last three varchars. If I try to get fancy and merge it all down as shown below, am I going to have problems?
public void CallStoredProcedure(string[] astrParams, string strConnectionString, string strStoredProcedure)
{
int nParams = 0;
SqlParameter[] asqlParams;
asqlParams = SqlHelperParameterCache.GetSpParameterSet(strConnectionString, strStoredProcedure);
foreach (string strParam in astrParams)
{
asqlParams[nParams].Value = strParam;
nParams++;
}
}
Alternately, can I use an array of mixed data types without know what is in there, and can I assign different types into the same array, replacing elements?
object[] aParams;
string strName = "Joe";
long lngHeight = 180;
object[0] = strName;
object[1] = lngHeight;
CallStoredProcedure(aParams, strConnectionString, "StoredProcedure1")
long lngWeight = 3;
string strItemName = "Bookend";
object[0] = lngWeight;
object[1] = strItemName;
CallStoredProcedure(aParams, strConnectionString, "StoredProcedure2")
And then change the code inside that function to:
foreach (object oParam in astrParams)
{
asqlParams[nParams].Value = oParam;
nParams++;
}
Would I need to use ToString() in either or both of these cases? And if so, does that essentially turn them into the same thing? Like I said, right now I've got three functions that take in all the parameters correctly typed, but I'm trying to get rid of duplicate code if possible.
You cannot have an array of different data types, you can have an array of objects and cast the individual elements to a specific data type when saving the values to typed variable though.
Not sure where you want to use ToString() but if its when you are saving a string into an Object array there is no point but if you are saving an object to a string variable then yes you would need to do this:
string str = objectArray[0].ToString();
I would avoid this whole mess though, and follow what DJ KRAZE said and add your parameters via Parameter.AddWithValue though.
I was fairly confused about what I needed to do with my initial question. What I realized is that I don't need to worry about C# types when adding the parameters, since GetSpParameterSet pulls all the information I need into the SqlParameter array, and resets the array every time I call it. So my initial code:
foreach (string strParam in astrParams)
{
asqlParams[nParams].Value = strParam;
nParams++;
}
...will just work for my needs. There won't be any problem assigning a C# string type into an SQL int type, which was my main concern, since the languages don't have identical types anyway. I know that I won't have to worry about anything like an "x" going into an int type because all the values I'm using are being pulled from the properly typed columns in other SQL queries already. So I won't need any object arrays.

LINQ statement no longer works after being wrapped into an extension method

I had a need for a method that could take a collection of strings, and replace all occurrences of a specific string with another.
For example, if I have a List<string> that looks like this:
List<string> strings = new List<string> { "a", "b", "delete", "c", "d", "delete" };
and I want to replace "delete" with "", I would use this LINQ statement:
strings = (from s in strings select (s=="delete" ? s=String.Empty : s)).ToList();
and it works great. But then I figured I should make it an extension method, since I'd likely use it again later. In this case, I just want to write the following:
strings.ReplaceStringInListWithAnother( "delete", String.Empty);
While my code compiles, and the LINQ statement works inside of the extension method, when I return the collection reverts back to its original contents:
public static void ReplaceStringInListWithAnother( this List<string> my_list, string to_replace, string replace_with)
{
my_list = (from s in my_list select (s==to_replace ? s=replace_with : s)).ToList();
}
So it would seem that I just modified a copy of the List... but when I looked at the code for Pop, it modifies the collection similarly, yet the changes stick, so my assumption was that my method's parameter declarations are correct.
Can anyone explain what I am doing wrong here?
The LINQ statement you wrote does not modify the collection, it actually creates a new one.
The extension method you wrote creates this new collection and then discards it. The assignment is redundant: you’re assigning to a local parameter, which goes out of scope immediately after.
When you’re calling the method, you’re also discarding its result instead of assigning it back.
Therefore, you should write the method like this:
public static List<string> ReplaceStringInListWithAnother(
this List<string> my_list, string to_replace, string replace_with)
{
return (from s in my_list select
(s == to_replace ? replace_with : s)).ToList();
}
and the call like this:
strings = strings.ReplaceStringInListWithAnother("delete", "");
By the way, you can make the function more useful by making it generic:
public static List<T> ReplaceInList<T>(this List<T> my_list,
T to_replace, T replace_with) where T : IEquatable<T>
{
return (from s in my_list select
(s.Equals(to_replace) ? replace_with : s)).ToList();
}
This way you can use it for other things, not just strings. Furthermore, you can also declare it to use IEnumerable<T> instead of List<T>:
public static IEnumerable<T> ReplaceItems<T>(this IEnumerable<T> my_list,
T to_replace, T replace_with) where T : IEquatable<T>
{
return from s in my_list select (s.Equals(to_replace) ? replace_with : s);
}
This way you can use it for any collection of equatable items, not just List<T>. Notice that List<T> implements IEnumerable<T>, so you can still pass a List into this function. If you want a list out, simply call .ToList() after the call to this one.
Update: If you actually want to replace elements in a list instead of creating a new one, you can still do that with an extension method, and it can still be generic, but you can’t use Linq and you can’t use IEnumerable<T>:
public static void ReplaceInList<T>(this List<T> my_list,
T to_replace, T replace_with) where T : IEquatable<T>
{
for (int i = 0; i < my_list.Count; i++)
if (my_list[i].Equals(to_replace))
my_list[i] = replace_with;
}
This will not return the new list, but instead modify the old one, so it has a void return type like your original.
Here's a hint: what do you expect the below code to do?
void SetToTen(int y)
{
y = 10;
}
int x = 0;
SetToTen(x);
Hopefully, you understand that the SetToTen method above does nothing meaningful, since it only changes the value of its own local variable y and has no effect on the variable whose value was passed to it (in order for that to happen, the y parameter would have to be of type ref int and the method would be called as SetToTen(ref x)).
Keeping in mind that extension methods are really just static methods in fancy clothes, it should be clear why your ReplaceStringInListWithAnother is not doing what you expected: it is only setting its local my_list variable to a new value, having no effect on the original List<string> passed to the method.
Now, it's worth mentioning that the only reason this is not working for you is that your code works by setting a variable to a new object*. If you were to modify the List<string> passed to ReplaceStringInListWithAnother, everything would work just fine:
public static void ReplaceStringInListWithAnother( this List<string> my_list, string to_replace, string replace_with)
{
for (int i = 0; i < my_list.Count; ++i)
{
if (my_list[i] == to_replace)
{
my_list[i] = replace_with;
}
}
}
It's also worth mentioning that List<string> is an overly restrictive parameter type for this method; you could achieve the same functionality for any type implementing IList<string> (and so I'd change the my_list parameter to be of type IList<string>).
*Reading your question again, it seems clear to me that this is the main point of confusion for you. The important thing you have to realize is that by default, everything in C# is passed by value. With value types (anything defined as a struct -- int, double, DateTime, and many more), the thing that's passed is the value itself. With reference types (anything that's defined as a class), the thing that's passed is a reference to an object. In the latter case, all method calls on references to objects of mutable types do actually affect the underlying object, since multiple variables of reference type can point to the same object. But assignment is different from a method call; if you assign a reference to an object that has been passed by value to some new reference to an object, you are doing nothing to the underlying object, and therefore nothing is happening that would be reflected by the original reference.
This is a really important concept that many .NET developers struggle with. But it's also a topic that's been explained thoroughly elsewhere. If you need more explanation, let me know and I'll try to dig up a link to a page that makes all of this as clear as possible.
You haven't shown the code for "Pop" so it's hard to know what you mean. You talk about "when I return the collection" but you're not returning anything - the method has a void return type.
LINQ typically doesn't change the contents of an existing collection. Usually you should return a new collection from the extension method. For example:
public static IEnumerable<string> ReplaceAll
(this IEnumerable<string> myList, string toReplace, string replaceWith)
{
return toReplace.Select(x => x == toReplace ? replaceWith : x);
}
(I've made it more general here - you shouldn't start materializing lists unless you really need to.)
You'd then call it with:
strings = strings.ReplaceAll("delete", "").ToList();
... or change the type of string to IEnumerable<string> and just use
strings = strings.ReplaceAll("delete", "");

Returning a value type from a property

I'm getting confused with what happens on the stack and heap in respect to value type properties in classes.
My understanding so far:
When you create a class with a structure (value type) like this:
class Foo
{
private Bar _BarStruct;
public Bar BarStruct
{
get {return _BarStruct; }
set {_BarStruct = value; }
}
}
private struct Bar
{
public int Number;
Bar()
{
Number = 1;
}
Bar(int i)
{
Number = i;
}
}
If you create a class instance like so:
Foo fooObj = new Foo();
The stack and heap will look like this:
...where the Bar structure is embeded in the Foo class in the heap. This makes sense to me, but I start to loose it when we consider modifying the Number integer in the BarStruct class, within the Foo Object. For example:
Foo fooObj = new Foo();
fooObj.BarStruct.Number = 1;
As I understand, this should be returning a copy of BarStruct to live on the stack, which means that any changes of a member of BarStruct would not be carried through to the object, which is why the last line above gives an error.
Is this right so far?
If so, my question is, how come an assignment such as this:
fooObj.BarStruct = new Bar(2);
...is valid and changes the heap value? Surely this is just changing the value on the stack?? Also, (by and by) I find it so confusing that you are able to use new on a value type. To me, new is for allocatting on the heap (as per C++) and feels unnatural to be doing this for items on the stack.
So just to re-iterate the question, Am I correct in my assumption of what happens when a property containing a structure is called and why can you assign a new structure to a copy and yet it still changes the reference on the heap?
Really hope this all make sense.
Yell if you need clarification!
Ta,
Andy.
Looking at this assignment:
fooObj.BarStruct = new Bar(2);
The assignment isn't changing the value on the stack - it's calling the setter for the property.
In other words, whereas your first assignment is equivalent to:
fooObj.get_BarStruct().Number = 1; // Bad
the second is equivalent to:
fooObj.set_BarStruct(new Bar(2));
Does that help?
Note that the problematic assignment becomes a non-issue if you make your value type immutable to start with - which helps in general, in fact. Mutable value types are a really bad idea in C#; you can get into no end of trouble with them.
In terms of your expectations of "new" - try not to think in C++, basically. C# isn't C++, and various things (destructors, generics, behaviour during construction) will confuse you if you try to effectively write C++ in C#. A "new" statement creates a new instance of a type, whether that's a value type or a reference type.

Categories

Resources