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; }
Related
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();
I have a
List<object> list = new List<object>();
while (myReader.Read())
{
string arrKablan = myReader["arrK_Title"].ToString();
string arrTotal = myReader["arrTotal"].ToString();
string _title = myReader["MF_Title"].ToString();
string _path = myReader["MF_Path"].ToString();
int _level = Convert.ToInt32(myReader["MF_Level"].ToString());
list.Add(new { title = _title, path = _path, kablanim = arrKablan, total = arrTotal, level = _level });
}
I need to select just items where level == 1
i tried
list = list.where(item => item.level == 1);
but i get an error
'object' does not contain a definition for 'level' and no extension method 'level' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?)
i know that the compiler can get the type so he can know what it is "level".
how can i achieve this kind of select, without to define a class ?
You have two ways of fixing this:
Use a List<dynamic> instead of a List<object>. This will disable type checks. Drawback: This will disable type checks. :-)
Let the compiler infer the correct type of your list. To do this, have your data layer return a DataTable instead of a DataReader and then use LINQ to create the list:
var myList = (from drow in myDataTable.AsEnumerable()
select new {
kablanim = drow["arrK_Title"].ToString(),
total = drow["arrTotal"].ToString(),
...
}).ToList();
I can't see why you don't just make a concrete class:
public class Foo
{
public string Title { get; set; }
public string Path { get; set; }
// etc, etc
}
Then
List<Foo> list = new List<Foo>();
while (myReader.Read())
{
string arrKablan = myReader["arrK_Title"].ToString();
string arrTotal = myReader["arrTotal"].ToString();
string _title = myReader["MF_Title"].ToString();
string _path = myReader["MF_Path"].ToString();
int _level = Convert.ToInt32(myReader["MF_Level"].ToString());
list.Add(new Foo { Title = _title, Path = _path, /* etc, etc */ });
}
then you call becomes
list = list.Where(item => item.Level == 1).ToList();
(Note the additional ToList call required to make the list assignment valid)
Just for completeness, you can also do this. Create a function to get a value from any object using reflection:
private T GetValue<T>(object obj, string property)
{
return (T)obj.GetType()
.GetProperties()
.Single(p => p.Name == property)
.GetValue(obj);
}
And call it like this:
var filteredList = list.Where(item => GetValue<int>(item, "level") == 1);
You can get value of a property on anonymous class like this:
var anon = new { Level = "level", Time = DateTime.Now };
Type type = anon.GetType();
var props = type.GetProperties();
foreach (var propertyInfo in props)
{
if (propertyInfo.Name == "Level")
{
var x =propertyInfo.GetValue(anon);
}
}
I'm not sure if it is the best way to achieve that, but it is certainly possible.
You are adding object of anonymous class to the list. You can refer to this anonymous class field only inside the method you've defined it in and you should probably avoid adding it to the list, because there is now other way other then reflection or dynamic to access field of theese objects.
For example, you can access one of the elements like this:
var list = new List();
list.Add(new { field1 = "a", field2 = 2 });
list.Add(new { field1 = "b", field2 = 3 });
list.Add(new { field1 = "c", field2 = 4 });
dynamic o = list[1];
Console.WriteLine(o.field1);
Console.WriteLine(o.field2);
But you should be aware, that dynamic feature has a big overhead on every member access.
If you really want to use lambdas, you can rewrite
list = list.where(item => item.level == 1);
like this
var filtered = list.Where(item =>
{
dynamic ditem = item;
return ditem.Level == 1;
});
but this is a bad approach.
The other approach is to use reflection and rewrite this lambda like this
var filtered = list.Where(item =>
{
var field = item.GetType().GetField("level");
return (int)field.GetValue(item) == 1;
});
This is better than using dynamic because it has a smaller overhead, but can still be very costly.
Also it would probably be better to cache FieldInfo object outside of loop if your anonymous objects have same type. It can be done like this
var field = list.First().GetType().GetField("level");
var filtered = list.Where(item => (int)field.GetValue(item) == 1);
For performance reasons, Linq depends on metadata being available at compile time. By explicitly declaring List<object> you have typed the elements of this list as object which does not have a member level.
If you want to use Linq like this you have two options.
Declare a class with a level member and use it to type the collection
Declare an interface with a level member and use it to cast in the lambda expression
Option 1 is the preferred approach. Normally Linq is used with a database and the classes are generated by Visual Studio directly from the database. This is why nobody complains about the need for classes to supply metadata.
The following line creates anonymous class.
new { title = _title, path = _path, kablanim = arrKablan, total = arrTotal, level = _level });
You can't cast then your objects to anything meaningfull.
Objects don't have those properties.
You have to create a class by your own and use it.
How to make the following code shorter, perhaps using anonymous method or extensions and LINQ.
Since I have to repeat this code several times and I want to make it as succinct as possible.
var imagesToUnlock = App.ImageListVM.Items.Where(img => img.Category == key);
foreach (var image in imagesToUnlock)
{
image.IsLocked = false;
}
The other solutions here feel dirty because they mutate objects in a collection via the use of LINQ.
I would instead, put the code and the filter condition into an extension method and call that:
public static IEnumerable<Item> UnlockWhere(this IEnumerable<Item> list, Func<Item, bool> condition) {
foreach (var image in list)
if (condition(image)) {
image.IsLocked = false;
yield return image;
}
}
The keeps the immutability-concerns of LINQ intact and still produces the expected result.
The call becomes:
var unlockedItems = App.ImageListVM.Items.UnlockWhere(img => img.Category == key);
EDIT
Re-written to completely remove LINQ. Instead, this new method iterates only once and returns a new, mutated collection.
Not the most efficient way to do it, but I believe you can do
var imagesToUnlock = App.ImageListVM.Items.Where(img => img.Category == key).ToList().Foreach(f => f.IsLocked = false);
Check out the Foreach method on List<T> for more info.
I would also like to note (as some have pointed out in the comments) that this is not considered best practice by some people. You should take a look at this article by Eric Lippert, who explains the issue in better detail.
Here's a stab as an extension method
Code
public static IEnumerable<T> SetPropertyValues<T>(this IEnumerable<T> items, Action<T> action)
{
foreach (var item in items)
{
action(item);
yield return item;
}
}
Usage
private class Foo
{
public string Bar { get; set; }
}
[TestMethod]
public void SetPropertyValuesForMiscTests()
{
var foos = new[] { new Foo { Bar = "hi" }, new Foo { Bar = "hello" } };
var newList = foos.SetPropertyValues(f => f.Bar = "bye");
Assert.AreEqual("bye", newList.ElementAt(0).Bar);
Assert.AreEqual("bye", newList.ElementAt(1).Bar);
}
I tested it and it works fine.
Yeah you can do this. Adapted from this answer.
imagesToUnlock.Select(i => {i.IsLocked = false; return i;}).ToList();
Edit: A lot of people are saying this is bad practice. I agree with dasblinkenlight here.. Exploring the limits of LINQ and C# is our duty as programmers. It isn't unreasonable to change the objects type from the DTO to the view model or domain object, I know its not the best, but if encapsulated and commented it isn't the end of the world to use select to do this. But please be conscious of the best practices explained by Eric.
I'm attempting to use Enumerable.OrderBy to sort a List because ultimately I want to be able to sort by more than a single field. At the moment it only appears to work if I create a new variable var to hold the results view which means (I think) the types need to be re-cast.
Is there a method to sort a List by more than 1 field whilst retaining the original List variable and types? I.e. I'd rather end up with variable _orderedbins of type List<orderedbins>
Below is what I currently have but everything from var test = ... onwards seems a bit wrong.
public class orderedBins
{
public string Bin { get; set; }
public int Order { get; set; }
}
List<orderedbins> _orderedbins = new List<orderedbins>();
foreach (string item in splitbins)
{
string[] spbinSetting = item.Split(',');
bool bchecked = bool.Parse(spbinSetting[1]);
int border = int.Parse(spbinSetting[2]);
if (bchecked == true)
{
_orderedbins.Add(new orderedbins { bin = spbinSetting[0], Order = border });
}
}
var test =_orderedbins.OrderBy(x => x.Order);
foreach (var item in test)
{
string f = item.Bin;
int g = item.Order;
}
You know, you can perform multiple sub-sorts for an order by...
lst.OrderBy(x => x.Prop1).ThenBy(x => x.Prop2).ThenByDescending(x => x.Prop3)...
Just add a .ToList(); and introduce it with a variable, to have the result in a list variable.
EDIT:
Great suggestion by Willem, for more readability:
from x in lst
order by x.Prop1, x.Prop2, x.Prop3
select x
You can create a new sorted list without creating a new variable using
list = list.OrderBy(item => item.Field1).ThenBy(item => item.Field1).ToList();
It will still create an entirely new list though (it's not actually much of a problem to add a new variable; those are cheap. Creating a new list, doing this, is fine as long as the list isn't very large.
If you need to sort the list in place then you'll want to use a custom comparer with the List's sort method:
public class MyComparer : IComparer<MyClass>
{
public int Compare(MyClass x, MyClass y)
{
if(x.Field1 != y.Field1)
return x.Field1.CompareTo(y.Field1)
else
return x.Field2.CompareTo(y.Field2);
}
}
List<MyClass> list = new List<MyClass>();
//Populate list
list.Sort(new MyComparer());
As others suggested, using Linq's OrderBy(...).ToList() would be a cleaner way, but this will give you a new instance of the list.
To retain the original instance, consider to use List<T>.Sort():
_orderedbins.Sort(new Comparison<orderedBins>((obj1, obj2) =>
{
int result = obj1.Order.CompareTo(obj2.Order);
return result != 0 ? result : obj1.Bin.CompareTo(obj2.Bin);
}));
This will do the trick:
_orderedbins = _orderedbins.OrderBy(x => x.Order).ToList();
...but there's no real issue creating a new variable/reference.
I think this will do it (it's already a list of orderbins so no casting is required):
_orderbins = _orderbins.OrderBy(x => x.Order).ToList();
With :
var Foo = new[]{ new {Something = 321}};
Why can I do (compile) :
Console.WriteLine( Foo[0].Something );
but not :
Foo.ForEach(x => Console.WriteLine(x.Something));
Because Array only have a static ForEach method:
var Foo = new[] { new { Something = 321 } };
Array.ForEach(Foo, x => Console.WriteLine(x.Something));
compiles and works.
try
Foo.ToList().ForEach(x => Console.WriteLine(x.Something));
instead, as the ForEach extension is only available for lists
EDIT: tested and works.
EDIT2: A few workarounds to make an "anonymous list"
This SO post
This blog post
Another blog post