Predefine field in class using several commands - c#

Let's say I have this class with a constructor that fills the internal list with two entries:
class MyClass
{
IList<int> someList;
public MyClass()
{
someList = new List<int>();
someList.Add(2);
someList.Add(4);
... // do some other stuff
}
}
Now let's say I have several constructors which all do the same with the internal list (but differ in other aspects).
I would like to know if I can outsource the generation and filling of the list directly to the field, like this:
class MyClass
{
IList<int> someList = new List<int>(); someList.Add(2); someList.Add(4);
// Does not compile.
public MyClass()
{
... // do some other stuff
}
}
Is it possible to call several commands in the field definition, and if yes, how?

You can pre-instantiated IList like this and add your values per accessing the Indexer:
IList<int> someList = new List<int>() { 2, 4 };
This will be initialization happens before the constructor is used.
Update 1
As OP mentioned in the comments, for LinkedList<T>() you have to use the constructor with some IEnumarable (in my Example an Array).
LinkedList<int> myList1 = new LinkedList<int>(new int[] {2,3,4});
Update 2
After reading your last comment, you're looking for Fluent Interfaces in your instantiation process. This is a method of chaining functions together and would look something like this:
Customer c1 = new Customer()
.FirstName("matt")
.LastName("lastname")
.Sex("male")
.Address("austria");
This functionality is not given by default in Collection Classes.You have to implement your own version of IList<T> for this.
Lambda Expression is a way to achieve this, like your update shows...

Got it:
IList<int> someList = new Func<List<int>>(() => { IList<int> l = new List<int>(); l.Add(2); l.Add(4); return l; })();
Explanation:
() => { IList<int> l = new List<int>(); l.Add(2); l.Add(4); return l; }
is a function taking no argument and returning an IList<int>, so it is a Func<IList<int>>.
Althoug the compiler knows this, it seems I explicitly have to state that fact via
new Func<IList<int>>(...)
to be able to call it later. The call is done as usual by putting two brackets () behind the Func.
Or to write it in a more readable way (then I don't even need the new keyword, but instead must make the Func static):
static Func<IList<int>> foo = () => { IList<int> l = new List<int>(); l.Add(2); l.Add(4); return l; };
IList<int> someList = foo();

Related

How to concatenate immutable collections efficiently?

I'm using System.Collections.Immutable and I want to find a way to concatenate several immutable collections without copying all the items (better than O(n)). All I need from resulting collection is IReadOnlyCollection<T> implementation.
My first idea was to use immutable double-linked list, but it seems that only prototypes of it exists over the Internet, and there's no reliable implementation. See, for example: Efficient implementation of immutable (double) LinkedList
Another idea is to create an immutable list of immutable lists and implement IReadOnlyCollection<T> on top of it. But again, it's a self-made solution for quite a popular problem, and I'm afraid I'm overlooking something.
IEnumerable's Concat() will return an enumerable implementation that simply enumerates the passed in enumerables without making a copy of them.
Be aware that a similar method IEnumerable Append allows adding single element to an enumerable.
Here is a passing test that verifies the original enumerable isn't ran when Concat and Append is called, execution is delayed until the concat or append result is enumerated (I wasn't sure given the wording of Append()'s documentation):
[Fact]
public void Test()
{
var selectClauseExecutionCount = 0;
var original = Enumerable.Range(1, 100);
var enumerated = original.Select(a =>
{
selectClauseExecutionCount++; ;
return a;
});
var concated = enumerated.Concat(new[] { 1, 2, 3 });
Assert.Equal(0, selectClauseExecutionCount);
var appended = concated.Append(5);
Assert.Equal(0, selectClauseExecutionCount);
Assert.Equal(5, appended.Last());
Assert.Equal(100, selectClauseExecutionCount);
}
If a double-linked immutable list or a list of lists is suitable for you then I'm guessing that you're just looking for a good way to merge and iterate any number of immutable lists as one, without creating unnecessary new copies of their elements.
From the docs you can see that IReadOnlyCollection<T> derives directly from IEnumerable<T> so if you can relax the constraint and have the resulting collection as IEnumerable<T> then your problem can be solved with LINQ and the ref keyword (as the parent interface is essentially readonly as well).
public IEnumerable<T> Concat<T>(params IReadOnlyCollection<T>[] things)
{
return things.SelectMany(x => x.Select(y => SelectByReference(ref y)));
}
private static ref T SelectByReference<T>(ref T t)
{
return ref t;
}
private void Example()
{
var c1 = new ReadOnlyCollection<string>(new[] { "1", "2" });
var c2 = new ReadOnlyCollection<string>(new[] { "3", "4" });
var c3 = new ReadOnlyCollection<string>(new[] { "5", "6" });
var resulting = Concat(c1, c2, c3);
foreach (var item in resulting)
{
// read the item etc without any copies being created
}
}

Passing a List into a method, modify the list within the method without affecting 'original'

Sorry if the subject seems vague, I tried summing it up as best I can without knowing the exact terminology of what I'm trying to achieve.
Essentially I have a list and then I call a method
public List<int> myList;
void Start () {
myList = new List<int>();
myList.Add (1);
myList.Add (2);
doSomething(myList);
foreach (int i in myList){
print (i);
}
}
In my method I'd like to do this (for example)
public void doSomething (List<int> myPassedList)
{
int A = 5;
myPassList.Add (A);
//... And then some other cool code with this modified list
}
However, I dont want the original list changed, I want it exactly as it was. Essentially when I pass the list into the method I'd like a duplicate of the list, which is then made new each time the method is called.
I want to see the console print '1' then '2'
but it will print '1', '2' and '5'
Hopefully this all makes sense! Thanks very much in advance for any help
Jim
List is a reference type so when you pass myPassedList as an argument to doSomething you are modifying the original list.
You have two options, either call ToList() or create a new list, as an example:
public void doSomething (List<int> myPassedList)
{
List<int> newList = myPassedList.ToList();
int A = 5;
newList.Add(A);
//... And then some other cool code with this modified list
}
The original list myList will then only return 1 and 2.
If you write a method that works with a list but will not modify that list, then you should document this by code with
public void doSomething ( IEnumerable<int> myPassedValues )
{
List<int> newList = myPassedValues.ToList();
int A = 5;
newList.Add(A);
//... And then some other cool code with this modified list
}
Now you and all others will know, just by reading the declaration that the passed list will not be modified in this method.
Inside your doSomething() method, create a duplicate list by:
var newList = new List<int>(myPassedList);
int A = 5;
newList.Add (A);
myPassedList will not be affected
You can simply copy all the items to a new list.
public void doSomething (List<int> myPassedList)
{
List<int> list = myPassedList.GetRange(0, myPassedList.Count);
}
Simply you can clone the original list in the function
public void doSomething (List<int> myPassedList)
{
List<int> clonedList = myPassedList;
// doing something here with clonedList
}

How to unchain items in list<t> class

can some one explain to me how to break the chain with a NEW statement?
Let me clarify the chain I’m talking about. When I call to a class I use the NEW statement like so
Myclass x =new Myclass();
My understanding is this creates a new empty instance of Myclass. Now correct me if I’m wrong but having a new empty instance one should be able to add what ever data the class supports?
I use this lot and would think the above to be true until adding data in such a manner
Myclass x =new Myclass();
//oldMyclass being old data that needs to be changed then
//added back to the class as a new or duplicate entry
x = oldMyclass[1];
//we change the data
x.red= 0x54;
//we add it back
oldMyclass.add(x);
All is good until we edit the data after adding it say we need to change another value.
We access the oldMyclass and select the proper item say its index is 2 but we only want to change the values of index 2
Myclass x =new Myclass();
x = oldMyclass[2];
x.red=soemvalue;
oldMyclass[2] = x;
This will change the red value of both index 1 and index 2. How can I break the chain between index 1 and index 2?
I think I might have over simplified this question let me know.
Thanks for any information.
Edit: Here is the copy method that I tried
public static Items.SavedItem Copy(Items.SavedItem old)
{
Items.SavedItem x = new Items.SavedItem();
x.generator = old.generator;
x.hireling_class = old.hireling_class;
x.id = old.id;
x.item_slot = old.item_slot;
x.owner_entity_id = old.owner_entity_id;
x.socket_id = old.socket_id;
x.square_index = old.square_index;
x.used_socket_count = old.used_socket_count;
return x;
}
So let's say, for arguments sake, you have a class like this:
public MyClass
{
public string Foo { get; set; }
}
And you have a collection
List<MyClass> myList = new List<MyClass>();
Now you create an instance of MyClass
MyClass obj1 = new MyClass() { Foo = "bar" };
Now if you do this:
myList.Add(obj1);
myList.Add(obj1);
You now have a list with TWO members, but they happen to be the same object. Whats stored in the list is a reference to the object you added, not the object itself. So myList[0] == myList[1]
Now if you did this:
MyClass item = myList[1];
And then:
item.Foo = "something else";
Both the item at index 1 and the item at index 0 will have 'Foo == "something else"' because they are the same item.
Another point that seems to be confusing you is this: myList has two items. If I do this:
MyClass item = myList[0];
myList still has two items. Indexing a collection doesn't remove it and because of that, there is no need to add the item back to the list. It's already there. All I've done is copy the reference from myList to a variable named item.
There are collections (Stack and Queue for example) that do work on the principle that you will remove items and (potentially) add them back, but List doesn't work that way.
So if you wanted to add multiple objects to myList you need to create multiple objects with the new keyword. For example:
List<MyClass> myList = new List<MyClass>();
MyClass obj1 = new MyClass() { Foo = "bar" };
myList.Add(obj1);
obj1 = new MyClass() { Foo = "something else" }; // Note: I've reused the variable, but this is a *new* object
myList.Add(obj1);
Or, if you don't need the new object assigned to a variable, you can simply if to:
List<MyClass> myList = new List<MyClass>();
myList.Add(new MyClass() { Foo = "a" });
myList.Add(new MyClass() { Foo = "b" });
Or even more compactly, you can exploit the collection initialization syntax and simply:
List<MyClass> myList = new List<MyClass>()
{
new MyClass() { Foo = "a" },
new MyClass() { Foo = "b" }
}
If you want to copy an object from your list, then you need to copy each property (and if it contains other objects, you may need to copy them too). There are various ways to do this, IClonable or a copy constructor are examples, but it basically comes down to, at some point, doing something like this:
myCopy.Foo = myOriginal.Foo;
myCopy.Bar = myOriginal.Bar;
// repeat for all properties that you want to copy.
Now assuming that Foo and Bar aren't also reference types, you have a copy. If they are reference types, you have a copy, but myCopy.Foo and myOriginal.Foo are still pointing at the same object.

Adding items to an IEnumerable through an extension method does not work?

In most of the methods I use that return some kind of collection I return IEnumerable rather than the specific type (e.g. List). In many cases I have another collection that I want to combine with the result IEnumerable, this would be exactly like taking a List and adding another List to it using the AddRange method. I have the following example, in it I have created an extension method that should take a collection of items to add and adds them to a base collection, while debugging this appears to works but in the original collection the items are never added. I don't understand this, why aren't they added, is there something about the implementation of the IEnumerable that I am missing? I understand that IEnumerable is a read only interface, but Iam not adding to this list in the example below, I am replacing it, but the original IEnumerable does not change.
class Program
{
static void Main(string[] args)
{
var collectionOne = new CollectionContainerOne();
var collectionTwo = new CollectionContainerTwo();
// Starts at 1- 50 //
collectionOne.Orders.AddRange(collectionTwo.Orders);
// Should now be 100 items but remains original 50 //
}
}
public class CollectionContainerOne
{
public IEnumerable<Order> Orders { get; set; }
public CollectionContainerOne()
{
var testIds = Enumerable.Range(1, 50);
var orders = new List<Order>();
foreach (int i in testIds)
{
orders.Add(new Order() { Id = i, Name = "Order #" + i.ToString() });
}
this.Orders = orders;
}
}
public class CollectionContainerTwo
{
public IEnumerable<Order> Orders { get; set; }
public CollectionContainerTwo()
{
var testIds = Enumerable.Range(51, 50);
var orders = new List<Order>();
foreach (int i in testIds)
{
orders.Add(new Order() { Id = i, Name = "Order #" + i.ToString() });
}
this.Orders = orders;
}
}
public class Order
{
public int Id { get; set; }
public string Name { get; set; }
public override string ToString()
{
return this.Name;
}
}
public static class IEnumerable
{
public static void AddRange<T>(this IEnumerable<T> enumerationToAddTo, IEnumerable<T> itemsToAdd)
{
var addingToList = enumerationToAddTo.ToList();
addingToList.AddRange(itemsToAdd);
// Neither of the following works //
enumerationToAddTo.Concat(addingToList);
// OR
enumerationToAddTo = addingToList;
// OR
enumerationToAddTo = new List<T>(addingToList);
}
}
You are modifying the parameter enumerationToAddTo, which is a reference. However, the reference is not itself passed by reference, so when you modify the reference, the change is not observable in the caller. Furthermore, it is not possible to use ref parameters in extension methods.
You are better off using Enumerable.Concat<T>. Alternatively, you can use ICollection, which has an Add(T) method. Unfortunately, List<T>.AddRange isn't defined in any interface.
Here is an example to illustrate the passing of reference types by reference. As Nikola points out, this is not really useful code. Don't try this at home!
void Caller()
{
// think of ss as a piece of paper that tells you where to find the list.
List<string> ss = new List<string> { "a", "b" };
//passing by value: we take another piece of paper and copy the information on ss to that piece of paper; we pass that to the method
DoNotReassign(ss);
//as this point, ss refers to the same list, that now contains { "a", "b", "c" }
//passing by reference: we pass the actual original piece of paper to the method.
Reassign(ref ss);
// now, ss refers to a different list, whose contents are { "x", "y", "z" }
}
void DoNotReassign(List<string> strings)
{
strings.Add("c");
strings = new List<string> { "x", "y", "z" }; // the caller will not see the change of reference
//in the piece of paper analogy, we have erased the piece of paper and written the location
//of the new list on it. Because this piece of paper is a copy of SS, the caller doesn't see the change.
}
void Reassign(ref List<string> strings)
{
strings.Add("d");
//at this point, strings contains { "a", "b", "c", "d" }, but we're about to throw that away:
strings = new List<string> { "x", "y", "z" };
//because strings is a reference to the caller's variable ss, the caller sees the reassignment to a new collection
//in the piece of paper analogy, when we erase the paper and put the new object's
//location on it, the caller sees that, because we are operating on the same
//piece of paper ("ss") as the caller
}
EDIT
Consider this program fragment:
string originalValue = "Hello, World!";
string workingCopy = originalValue;
workingCopy = workingCopy.Substring(0, workingCopy.Length - 1);
workingCopy = workingCopy + "?";
Console.WriteLine(originalValue.Equals("Hello, World!"); // writes "True"
Console.WriteLine(originalValue.Equals(workingCopy); // writes "False"
If your assumption about reference types were true, the output would be "False" then "True"
Calling your extensions method like this:
collectionOne.Orders.AddRange(collectionTwo.Orders);
Is essentially the same as:
IEnumerable.AddRange(collectionOne.Orders, collectionTwo.Orders);
Now what happens there, is you pass copy of reference to the collectionOne.Orders to the AddRange method. In your AddRange implementation you try to assign new value to the copy. It gets "lost" inside. You are not assigning new value to collectionOne.Orders, you assign it to its local copy - which scope is only within the method body itself. As a result of all modifications happenining inside AddRange, outside world notices no changes.
You either need to return new enumerable, or work on lists directly. Having mutating methods on IEnumerable<T> is rather counterintuitive, I'd stay away from doing that.
What you want exists and is called Concat. Essentially, when you do this in your Main:
var combined = collectionOne.Orders.Concat(collectionTwo.Orders);
Here, combined will refer to an IEnumerable that will traverse both source collections when enumerated.
IEnumerable does not support adding. What you in essence did in your code is create new collection from your enumerable, and add items to that new collection. Your old collection still has same items.
E.g., you create a collection of numbers like this
Collection1 = [ 1, 2, 3, 4, 5 ]
when you do Collection1.ToList().Add(...) you will get new collection with same members, and add new members like so:
Collection1 = [ 1, 2, 3, 4, 5, 6, 7, ... ]
your old collection will however still hold old members, as ToList creates new collection.
Solution #1:
Instead of using IEnumerable use IList which supports modification.
Solution #2 (bad):
Cast your IEnumerable back to it's derived type and add members to it. This is quite bad though, in fact it's better to just return List in the first place
IEnumerable<Order> collectionOne = ...;
List<Order> collectionOneList = (List<Order>)collectionOne;
collectionOneList.Add(new Order());
General guideline (best):
If you are returning collections which are standard in .NET there is no reason to return their interfaces. In this case it's best to use original type. If you are however returning collection which you implemented yourself, then you should return an interface
It's a completely different case when you are thinking about input parameters. If your method is asking to enumerate over items, then you should ask for IEnumerable. This way you can do what you need over it, and you are placing least constraint on person who is calling it. They can send any enumerable. If you need to add to that collection, you may require IList so that you can also modify it in your method.
Basically the problem is that you can't assign a value to enumerationToAddTo partially because it isn't a reference parameter. Also as phoog mentions ToList() creates a new list and does not cast the existing IEnumerable to a list.
This isn't really a good use of a extension. I would recommend that you add a method to your container collection that allows you add add new items to the IEnumerable instance. This would better encapsulate the logic that's particular to that class.

Set multiple properties in a List<T> ForEach()?

Given a class:
class foo
{
public string a = "";
public int b = 0;
}
Then a generic list of them:
var list = new List<foo>(new []{new foo(), new foo()});
If I am to assign multiple properties inside the following List<T> ForEach() method, is there a simpler way to do it that below? Hopefully I'm being a bit thick.
// one property - easy peasy
list.ForEach(lambda => lambda.a="hello!");
// multiple properties - hmm
list.ForEach(lambda => new Action(delegate() { lambda.a = "hello!"; lambda.b = 99;}).Invoke());
Edit: Thought ForEach() was a LINQ extension method, when it's actually part of List<T> oops!
All you need to do is introduce some brackets so that your anonymous method can support multiple lines:
list.ForEach(i => { i.a = "hello!"; i.b = 99; });
Anonymous method is your friend
list.ForEach(item =>
{
item.a = "hello!";
item.b = 99;
});
MSDN:
Anonymous Methods (C# Programming Guide)
list.ForEach(lamba=>lambda.a="hello!");
Becomes
list.ForEach(item=>{
item.a = "hello!";
item.b = 99;
});
Of course you can also assign them when you create the list like :
var list = new List<foo>(new []{new foo(){a="hello!",b=99}, new foo(){a="hello2",b=88}});
list.ForEach(i => i.DoStuff());
public void DoStuff(this foo lambda)
{
lambda.a="hello!";
lambda.b=99;
}
Honestly, there's really no need to use List.ForEach here:
foreach (var item in list) { item.a="hello!"; item.b=99; }

Categories

Resources