List not changed outside the method - c#

I noticed interesting action, when I change list inside some method, and throw exception inside this method, then out of scope of the method list is not changed.
How to change force this list to be changed in catch block? using out ?
List<string> list = null;
try
{
list = new List<string>{"1", "2", "3"};
ChangeMyList(list);
}
catch
{
//here list has 3 elements , why ?
}
void ChangeMyList(List<string> list)
{
list = list.Except(new List<string>{"1", "2"}).ToList();
//here this list has only one element
throw new Exception();
}

Inside ChangeMyList, list is a copy of the reference to the source list pointed to by list in the outer scope. Assigning to this local reference does not affect the reference in the caller. You can use ref to pass list by reference:
void ChangeMyList(ref List<string> list)
{
list = list.Except(new List<string>("1", "2")).ToList();
//here this list has only one element
throw new Exception();
}
then
List<string> list = new List<string>{"1", "2", "3"};
ChangeMyList(ref list);

This is not because of the exception; your list is not being changed because the reference to it is copied when you call the function, as in, list in your function is a copy of list in the parent scope, and any changes to it won't be made to the original one.
You have two solutions:
Either make your function return list, and store it back into the original variable:
try {
having List<string> list = new List<string>{"1", "2", "3"};
list = ChangeMyList(list);
} catch() {
}
List<string> ChangeMyList(List<string> list)
{
list = list.Except(new List<string>("1", "2"));
return list;
}
Or you can simply pass your list with the ref keyword to indicate that the function will modify it:
try {
having List<string> list = new List<string>{"1", "2", "3"};
ChangeMyList(ref list);
} catch() {
}
void ChangeMyList(ref List<string> list)
{
list = list.Except(new List<string>("1", "2"));
}

The answer was already given but just for those who, like me, understand it better in a graphical way:

Related

Is it possible to create a property from a string in C#?

I have a list of strings in C# and I want to loop through the list and add each item of the list as a property of my class.
For eg:
public class test
{
List<string> inputList = new List<string> {"add", "subtract", "multiply"};
foreach(var val in inputList)
{
//code to convert string to property
}
}
After I run the above code, when I create a new object of class test, I would like to get:
test.add or test.subtract etc. and I should be able to assign values to these properties.
Is this possible? If yes, could someone please suggest the best way of doing this?
Continuing from above, the aim here is to add API.
The list here is dynamically loaded. I should have started with a more apt list as an example.
public class test
{
List<string> inputList = new List<string> {"name", "age", "dob"};
foreach(var val in inputList)
{
//code to convert string to property of the class test
}
}
After the code is run I should be able to assign values to name, age and dob as user inputs i.e
test.name = "blah"
test.age = "123"
Since the list is dynamically updated, the items (and their number) in the list will vary. All the items in the list have to be added as a property of the class test and the user should be able to assign values to those properties at run time.
You can use dynamic to achieve this. While it's very good to be aware of the concept of dynamic and how to use it, it kills the benefits of using a strongly typed language and compile time checking, and should really only be used when the alternative is more painful:
List<string> inputList = new List<string> {"add", "subtract", "multiply"};
var builder = new System.Dynamic.ExpandoObject() as IDictionary<string, Object>;
foreach(var val in inputList)
{
builder.Add(val, $"{val}: some value here");
}
var output = (dynamic)builder;
Console.WriteLine(output.add);
Console.WriteLine(output.subtract);
Console.WriteLine(output.multiply);

find if there is a common string between 2 list of strings using linq

I have two Lists of strings:
List<string> lotterynumber;
List<string> lotterywinningnumber;
I want to see if a lotterynumber is within the lotterywinningnumber.
I am using a foreach loop right now:
bool b = false;
foreach(string s in lotterynumber)
{
if(lotterywinningnumber.contains(s))
{
b= true;
}
}
Is there a way I can do it in Linq?
You can do this using Enumerable.Intersect. It will return a new Enumerable containing the items that exist in both collections.
var lotteryNumbers = new List<string>() { "1", "2", "3" };
var lotteryWinningNumbers = new List<string>() { "2", "3", "5" };
var numbersInBoth = lotteryNumbers.Intersect(lotteryWinningNumbers); // { "2", "3" }
From MSDN:
The intersection of two sets A and B is defined as the set that contains all the elements of A that also appear in B, but no other elements. When the object returned by this method is enumerated, Intersect enumerates first, collecting all distinct elements of that sequence. It then enumerates second, marking those elements that occur in both sequences. Finally, the marked elements are yielded in the order in which they were collected.
The benefit to using Intersect is that it will return the values that exist in both collections, instead of just a Boolean value.
Obviously, if a Boolean value is what you need, you just have to check if the resulting collection contains any elements:
bool contains = lotteryNumbers.Intersect(lotteryWinningNumbers).Any();
There is a way, but the efficiency will be the same: O(n).
Use Any
bool b = lotterynumber.Any(a => lotterywinningnumber.Contains(a));

Add one list to another in C#

I'm trying to add one list to another . So I have this main list that I'm going to build. I loop through records and built a list and want this list to main list every time I loop. I'm trying to do this in C#.
I'm using following code. Add() function is not working.I'm getting syntax error.
IList<CgValDetail> cgValDetail = null;
//Get cgValDetails for each control
foreach (UiControlScreenMetaData tempUiControls in uiControls)
{
if (tempUiControls.CgValId == null)
{
continue;
}
IList<CgValDetail> tempCgValDetail = Retrieval<CgValDetail>.Search(new { CgValId = tempUiControls.CgValId }).ToList();
if (!tempCgValDetail.Any())
{
_foundationService.LogBusinessError(null, new ParameterBuilder("CgValId", tempUiControls.CgValId), "Invalid_CgValId_found");
return false;
}
//Add tempCgValDetail List to main list which is cgValDetail
cgValDetail.Add(tempCgValDetail);
}
Take a look at AddRange.
var firstList = new List<string>();
var secondList = new List<string>() { "a", "b" };
firstList.AddRange(secondList);
You mentioned that you don't have access to AddRange... The problem is that you're using an IList, which doesn't implement AddRange. Check this out for more on why: Why doesn't IList support AddRange
I would advise you to switch to List.
First, you need to instantiate cgValDetail as a new list, not a null.
Then you should decide if you want to declare cgValDetail as IList instead of a List. If yes, try this:
IList<CgValDetail> cgValDetail = new List<CgValDetail>();
//Get cgValDetails for each control
foreach (UiControlScreenMetaData tempUiControls in uiControls)
{
if (tempUiControls.CgValId == null)
{
continue;
}
IList<CgValDetail> tempCgValDetail = Retrieval<CgValDetail>.Search(new { CgValId = tempUiControls.CgValId }).ToList();
if (!tempCgValDetail.Any())
{
_foundationService.LogBusinessError(null, new ParameterBuilder("CgValId", tempUiControls.CgValId), "Invalid_CgValId_found");
return false;
}
//Add tempCgValDetail List to main list which is cgValDetail
((List<CgValDetail>)cgValDetail).AddRange(tempCgValDetail);
}
But I wonder why just not using a List instead of IList.
List<CgValDetail> cgValDetail = new List<CgValDetail>();
//Get cgValDetails for each control
foreach (UiControlScreenMetaData tempUiControls in uiControls)
{
if (tempUiControls.CgValId == null)
{
continue;
}
List<CgValDetail> tempCgValDetail = Retrieval<CgValDetail>.Search(new { CgValId = tempUiControls.CgValId }).ToList();
if (!tempCgValDetail.Any())
{
_foundationService.LogBusinessError(null, new ParameterBuilder("CgValId", tempUiControls.CgValId), "Invalid_CgValId_found");
return false;
}
//Add tempCgValDetail List to main list which is cgValDetail
cgValDetail.AddRange(tempCgValDetail);
}
Your cgValDetail is null. That's why when you add(...), you get syntax error.
Create a new IList<CgValDetail>
IList<CgValDetail> cgValDetail = new List<CgValDetail>();
Why didn't you use List<T> in stead of IList<T>?
You forgot to new it.
Just a simple example:
List<a> aList= new List<a>();
List<aList> List_aList = new List<aList>();
List_aList.add(new aList());
Here the link to same question.
Maybe, you can use "UNION" in Linq (if you don't really care about performance or result set is not big enough).
cgValDetail.Add(tempCgValDetail);
change to
cgValDetail = cgValDetail.Union(tempCgValDetail.Select(a => a)).ToList();

When I add a new item to a List<List<string>> each item of the parent list get the same values

Apologies if the answer to this is obvious, I'm fairly new to C# and OOP. I've stepped though my code and spent quite some time on Google but I can't find the answer to my question (quite possibly because I am using the wrong search terms!).
I have the following class that creates a static List<List<string>> and has a method to add items to that list:
public static class WordList
{
static List<List<string>> _WordList; // Static List instance
static WordList()
{
//
// Allocate the list.
//
_WordList = new List<List<string>>();
}
public static void Record(List<string> Words)
{
//
// Record this value in the list.
//
_WordList.Add(Words);
}
}
Else where I create a List<string> which I pass into the Record() method to be added to _WordList. The problem is when I add items to WordList it gives every item in that list the same value. e.g.:
1st item added contains "Foo" and "bar"
2nd item added contains "Not","Foo" and "bar"
So instead of a list that looks like:
1: "Foo","bar"
2: "Not","Foo","bar"
I end up with:
1: "Not","Foo","bar"
2: "Not","Foo","bar"
I haven't used a List<string[]> instead of a List<List<string>> because the way I am getting the List<string> to add is by reading a text file line by line with a delimiter saying when I should add the List<string> and clear it so I can start again. Therefore I don't know how long an array I need to declare.
Hope this makes some kind of sense! If you need anymore of the code posting to help let me know.
Thanks, in advance.
EDIT
Here is the code for the creation of the List<string> that is passed to the Record() method. I think I see what people are saying about not creating a new instance of the List<string> but I'm not sure how to remedy this in regards to my code. I will have a think about it and post an answer if I come up with one!
public static void LoadWordList(string path)
{
string line;
List<string> WordsToAdd = new List<string>();
StreamReader file = new System.IO.StreamReader(path);
while ((line = file.ReadLine()) != null)
{
if (line.Substring(0, 1) == "$")
{
WordList.Record(WordsToAdd);
WordsToAdd.Clear();
WordsToAdd.Add(line.Replace("$", ""));
}
else
{
WordsToAdd.Add(line.Replace("_"," "));
}
}
file.Close();
}
Instead of
WordList.Record(WordsToAdd);
WordsToAdd.Clear();
WordsToAdd.Add(line.Replace("$", ""));
do
WordList.Record(WordsToAdd);
WordsToAdd = new List<string>();
WordsToAdd.Add(line.Replace("$", ""));
All that your Record method is doing is adding a reference to the List<string> you've passed to it. You then clear that same list, and start adding different strings to it.
Maybe something like:
public static void Record(IEnumerable<string> Words)
{
_WordList.Add(Words.ToList());
}
Which will force a copy to occur; also, by accepting IEnumerable<string>, it puts less restrictions on the code that calls it.
Can you post the code that adds the list - I bet you are doing something like
create a list l
add it
modify l
add it
This result in a single object (because you created it only once) with multiple references to it, namely from the first value in _WordList, from the second value in _WordList, from l.
So the right way to do it is:
create list l
add it
create NEW list l
add it
Or in code:
List<string> l = new string[] { "Foo", "bar" }.ToList();
WordList.Record(l);
l = new string[] { "Not", "Foo", "bar" }.ToList();
WordList.Record(l);
You haven't shown how you are adding items to the list. Here's an example which works as expected:
using System;
using System.Collections.Generic;
using System.Linq;
public static class WordList
{
static List<List<string>> _WordList; // Static List instance
static WordList()
{
_WordList = new List<List<string>>();
}
public static void Record(List<string> Words)
{
_WordList.Add(Words);
}
public static void Print()
{
foreach (var item in _WordList)
{
Console.WriteLine("-----");
Console.WriteLine(string.Join(",", item.ToArray()));
}
}
}
class Program
{
static void Main()
{
WordList.Record(new[] { "Foo", "bar" }.ToList());
WordList.Record(new[] { "Not", "Foo", "bar" }.ToList());
WordList.Print();
}
}

How do I copy items from list to list without foreach?

How do I transfer the items contained in one List to another in C# without using foreach?
You could try this:
List<Int32> copy = new List<Int32>(original);
or if you're using C# 3 and .NET 3.5, with Linq, you can do this:
List<Int32> copy = original.ToList();
I see that this answer is still getting upvotes. Well, here's a secret for ya: the above answer is still using a foreach. Please don't upvote this any further.
To add the contents of one list to another list which already exists, you can use:
targetList.AddRange(sourceList);
If you're just wanting to create a new copy of the list, see the top answer.
For a list of elements
List<string> lstTest = new List<string>();
lstTest.Add("test1");
lstTest.Add("test2");
lstTest.Add("test3");
lstTest.Add("test4");
lstTest.Add("test5");
lstTest.Add("test6");
If you want to copy all the elements
List<string> lstNew = new List<string>();
lstNew.AddRange(lstTest);
If you want to copy the first 3 elements
List<string> lstNew = lstTest.GetRange(0, 3);
And this is if copying a single property to another list is needed:
targetList.AddRange(sourceList.Select(i => i.NeededProperty));
This method will create a copy of your list but your type should be serializable.
Use:
List<Student> lstStudent = db.Students.Where(s => s.DOB < DateTime.Now).ToList().CopyList();
Method:
public static List<T> CopyList<T>(this List<T> lst)
{
List<T> lstCopy = new List<T>();
foreach (var item in lst)
{
using (MemoryStream stream = new MemoryStream())
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, item);
stream.Position = 0;
lstCopy.Add((T)formatter.Deserialize(stream));
}
}
return lstCopy;
}
Easy to map different set of list by linq without for loop
var List1= new List<Entities1>();
var List2= new List<Entities2>();
var List2 = List1.Select(p => new Entities2
{
EntityCode = p.EntityCode,
EntityId = p.EntityId,
EntityName = p.EntityName
}).ToList();
Adding to the top answers, if you want copies of "the objects in the list", then you can use Select and make the copies. (While the other answers make "a copy of a list", this answer makes "a list of copies").
Suppose your item has a Copy method:
List<MyObject> newList = oldList.Select(item => item.Copy()).ToList();
Or that you can create a new object from the previous one with a constructor:
List<MyObject> newList = oldList.Select(item => new MyObject(item)).ToList();
The result of Select is an IEnumerable<MyObject> that you can also pass to AddRange for instance, if your goal is to add to an existing list.
OK this is working well
From the suggestions above GetRange( ) does not work for me with a list as an argument...so sweetening things up a bit from posts above: ( thanks everyone :)
/* Where __strBuf is a string list used as a dumping ground for data */
public List < string > pullStrLst( )
{
List < string > lst;
lst = __strBuf.GetRange( 0, __strBuf.Count );
__strBuf.Clear( );
return( lst );
}
public static List<string> GetClone(this List<string> source)
{
return source.Select(item => (string)item.Clone()).ToList();
}
Here another method but it is little worse compare to other.
List<int> i=original.Take(original.count).ToList();

Categories