I'm being given a collection of Lazy items. I then want to forcibly 'create' them all in one go.
void Test(IEnumerable<Lazy<MailMessage>> items){
}
Normally with a Lazy item, the contained object won't be created until one of it's member's is accessed.
Seeing as there is no ForceCreate() method (or similar), I am forced to do the following:
var createdItems = items.Where(a => a.Value != null && a.Value.ToString() != null).Select(a => a.Value);
Which is using ToString() to force created each item.
Is there a neater way to forcibly create all items?
To get the list of all the lazily initialized values:
var created = items.Select(c => c.Value).ToList();
You need two things to create all the lazy items, you need to enumerate all items (but not necessarily keep them), and you need to use the Value property to cause the item to be created.
items.All(x => x.Value != null);
The All method needs to look at all values to determine the result, so that will cause all items to be enumerated (whatever the actual type of the collection might be), and using the Value property on each item will cause it to create its object. (The != null part is just to make a value that the All method is comfortable with.)
Seeing as there is no ForceCreate() method (or similar)
You can always create a ForceCreate() extension method on Lazy<T> for this:
public static class LazyExtensions
{
public static Lazy<T> ForceCreate<T>(this Lazy<T> lazy)
{
if (lazy == null) throw new ArgumentNullException(nameof(lazy));
_ = lazy.Value;
return lazy;
}
}
...accompanied by a ForEach extension method on IEnumerable<T>:
public static class EnumerableExtensions
{
public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action)
{
if (enumerable == null) throw new ArgumentNullException(nameof(enumerable));
if (action == null) throw new ArgumentNullException(nameof(action));
foreach (var item in enumerable)
{
action(item);
}
}
}
By combining these two extension methods you can then forcibly create them all in one go:
items.ForEach(x => x.ForceCreate());
Related
Is it possible to use IEnumerable FirstOrDefault within a List extension method? I am getting the error 'List does not contain a definition for FirstOrDefault'?
using LanguageExt;
using System;
using System.Linq;
public static class Question
{
public static Option<T> Lookup<T>(this List<T> enumerable, Func<T, bool> predicate)
{
if (enumerable == null)
{
return Option<T>.None;
}
var val = enumerable.FirstOrDefault(predicate);
return val == null ? Option<T>.None : Option<T>.Some(val);
}
public static void Run()
{
bool isOdd(int i) => i % 2 == 1;
var none = new List<int>().Lookup(isOdd); // => None
var some = new List<int> { 1 }.Lookup(isOdd); // => Some(1)
}
}
Normally it is possible to use FirstOrDefault on any enumerable sequence, so also on Lists.
The following works:
Func<int, bool> predicate = isOdd;
List<int> integerSequence = new List<int>();
var a = integerSequence.FirstOrDefault();
var b = integerSequence.Where(i => predicate(i)).FirstOrDefault();
var c = integerSequence.FirstOrDefault(i => predicate(i));
So I can't reproduce your problem. I see other problems though.
If you don't understand the this keyword, read Extension Methods demystified. Whenever you think: damn, I wish they had thought of this LINQ method, consider creating an extension method.
So your Lookup method is an extension method that takes a list and a predicate as input.. Since it seems that it can work on any enumerable sequence, let's not limit ourselves to List<T>, let's accept any enumerable sequence, like Arrays, Dictionaries, Lookup-tables, etc.
Furthermore, LINQ methods are most reusable if you let them return a sequence or result items, and let your called decide whether he wants all items, or only the FirstOrDefault, or maybe the Last, or Average.
So if you decide to create an extension method that takes an enumerable sequence as input, let it return an Enumerable sequence whenever possible, even if the sequence is empty: avoid returning null, if you mean: there are no elements matching what you want. Because that would mean that callers should have to check the result before they can continue concatenating other LINQ methods.
Finally: LINQ methods usually don't except null sources as input, they throw exceptions. LINQ methods expect callers to make sure the input is not null. Of course you are free to deviate from this, but callers don't expect it when using LINQ like methods.
With these guidelines in mind, consider to change your extension method.
Apparently, if an element in your input sequence equals null, you want to return Option<T>.None, otherwise you want Option<T>.Some(val)
public static Option<T> ToOption<T>(this T sourceElement)
{
return sourceElement?.Option<T>.Some(sourceElement) ?? Option<T>.None;
}
In words: if sourceElement equals null, return Option<T>.Some(sourceElement), otherwise return Option<T>.None
In LINQ it is quite normal to create extension methods with equality comparers, this will make your methods even more reusable. Usually this will only need two or three lines of code:
public static Option<T> ToOption<T>(this T sourceElement)
{
return ToOption(sourceElement, null);
}
public static Option<T> ToOption<T>(
this T sourceElement,
IEqualityComparer<T> comparer)
{
if (comparer == null) comparer = EqualityComparer<T>.Default;
if (comparer.Equals(sourceElement, null)
return Option<T>.None;
else
return Option<T>.Some(sourceElement);
}
In the methods below I won't mention this possibility over and over again.
public static IEnumerable<Option<T>> ToOptions<T>(this IEnumerable<T> source)
{
// TODO: throw exception if input null
return source.Select(sourceElement => sourceElement.ToOption());
}
public static IEnumerable<Option<T>> ToOptions<T>(
this IEnumerable<T> source,
Func<T, bool> predicate)
{
// No need: check if source == null, this is done in the other ToOptions
// TODO: exception if predicate equals null
return source.Where(sourceElement => predicate(sourceElement))
.ToOptions();
}
Simple usage:
IEnumerable<Customer> customers = ...
MyClass myFirstEligableCustomer= customers
.ToOptions(customer => customer.Address.City == "New York")
.FirstOrDefault();
Or intertwined with other LINQ methods:
var result = customers.
.GroupBy(customer => customer.Birthday.Year)
.OrderBy(customerGroup => customerGroup.Key)
.ToOptions(customerGroup => customerGroup.Key)
.ToDictionary(customerGroup => customerGroup.Key, // Key
customerGroup => customerGroup.ToList); // Value
The result is a Dictionary, where the key is the Year of a birthday, and the value is the list of all Option<Customer> that are born in this year.
You see: most methods are barely more than one-liners. They are easy to reuse, easy to change, easy to test.
It may be simple but nevertheless it has caused us a bit of headache over the past few hours.
Long story short: We need to fix a memory leak and the way to do it is to return the original list without creating a new instance of MyBindingList if ToBindingList is invoked on a list that is already of the type MyBindingList
MyBindingList inherits System.ComponentModel.BindingList
How would you write the extension method ToBindingList() allowing this test passes?
[TestMethod]
public void FooBar()
{
var SUT = new MyBindingList<FooBar>
{
new FooBar{Name = "AAA"},
new FooBar{Name = "BBB"},
};
var filteredList = SUT.Where(x => x.Name == "AAA").ToBindingList();
Assert.AreEqual(1, filteredList.Count);
Assert.AreEqual(true, ReferenceEquals(filteredList, SUT));
}
private class FooBar
{
public string Name { get; set; }
}
MyBindingList constructor goes like this
public class MyBindingList<T> : BindingList<T>, IEntityChanged
{
public MyBindingList(IList<T> list) : base(list) { }
//...
}
The problem we encounter is that the extension method operates on an iterator (the Where-clause) so we have no way of comparing the type informatoin of the two lists. We wrote the following extension method and then got wiser - and stuck:
public static MyBindingList<T> ToBindingList<T>(this IEnumerable<T> container)
{
var returnVal = container as MyBindingList<T>;
if (returnVal != null)
return returnVal;
return new MyBindingList<T>(container.ToList());
}
Can anybody help us here to a viable solution or explain why the compiler will never allow us to do something like this?
Thanks in advance
BindingList<T> (and more recently, ObservableCollection<T>) are just not for this purpose. They meant to wrap a 'final' result of a collection provided by your ViewModel or whatever underlying layer. If you need to filter the list you have basically two options:*
Return a new instance of the of the BindingList<T> (wrapping simple collections instead of another binding list) and rebind your view.
Handle the 'filtering' by removing the items from the binding list and just allow the view to update accordingly as it processes the notifications of the list change (after all that's why one uses an observable collection such as BindingList<T>).
*Remark: If your View uses WinForms technology, you can use the BindingSource type in the form (its DataSource can be your binding list), which also implements IBindingList. It has a Filter property, which is a string and can accept fancy expressions but actually I would not use this property in practice.
Looking at the source code, you can do this using Reflection:
var data = new List<int>();
var iterator = data.Where(x => 1 == 1);
var type = iterator.GetType();
var sourceField = type.GetField("source", System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance);
Console.WriteLine(sourceField.FieldType);
Which prints:
System.Collections.Generic.List`1[System.Int32]
You can test this on this fiddle.
So you could do something like this to get the value:
public static List<T> GetOriginalList<T>(this IEnumerable<T> whereSource)
{
var type = whereSource.GetType();
var sourceField = type.GetField("source",
System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.GetField);
return sourceField as List<T>;
}
public static MyBindingList<T> ToBindingList<T>(this IEnumerable<T> container)
{
var returnVal = container as MyBindingList<T>;
if (returnVal != null)
return returnVal;
return new MyBindingList<T>(container.GetOriginalList<T>());
}
Definitely possible, but hacky. Use reflection to check if the type is a nested type whose parent is System.Linq.Enumerable. If so, use reflection to get the value of the private 'source' instance field. That is the original source of the Where. This should work for all Enumerable methods, but I recommend you do this in a loop until you've reached the root source to support more complex querying. I also recommend caching reflection results in a static place. Fair warning though - there's no guarantee that 'source' will continue to be the name of the private field in upcoming releases. To rely on it means you need to re-test this for each and every .NET framework release you intend to run it on. It might just stop working one day.
I want to create a method that is able to add an item to a collection whose type is unknown at compile time. It will receive two objects: the collection and the item.
Currently, I have this:
public void Add(object collection, object item)
{
var list = (IList)collection;
list.Add(item);
}
The problem? It doesn't work when the collection is a, for example, an instance of this type:
public sealed class ColumnDefinitionCollection : IList<ColumnDefinition>, IEnumerable<ColumnDefinition>
{
}
What can I do to make it work with every kind of instance that has an Add method?
EDIT: I'm tried with the method proposed by #lukegv, but I'm getting this when the collection is a ColumnDefinitionCollection (running in a Universal Windows Application):
I created another question related to this issue ("Add" not being retrieved) Unable to get the "Add" method of a ColumnDefinitionCollection in UWP
I tried to extend Zdravko Danevs answer by some checks to prevent undesired behavior like the one you and Jon Skeet mentioned (DateTime.Add).
This one checks whether the type expected by the collections Add-method equals the type provided by the item.
static void AddToCollection(object collection, object item)
{
MethodInfo addMethod = collection.GetType().GetMethod("Add");
if (addMethod == null || addMethod.GetParameters().Length != 1)
{
// handle your error
return;
}
ParameterInfo parameter = addMethod.GetParameters().First();
if (parameter.ParameterType.Equals(item.GetType()))
{
addMethod.Invoke(collection, new object[] { item });
}
else
{
// handle your error
}
}
While this is probably not what you want to do, you can use reflection to check if there is an Add method and call it with the parameter supplied. Something like this (untested):
Type t = collection.GetType();
MethodInfo add = t.GetMethod("Add");
if (add != null)
{
params = new object[] { item };
add.Invoke(collection, params);
}
This code doesn't work, but:
public virtual ICollection<SomeItem> items { get { return (ICollection<SomeItem>)items.Where(e => e.isVisible == true); } set { ;} }
I'd like to do something to that effect. So to get an ICollection filtered by a property of the collection's elements.
Sure, I could iterate through the elements, and get the right ones, put them in a new collection and return with that, but is there a nicer solution?
Perhaps what you're looking for is an Extension Method?
Extension methods enable you to "add" methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type.
public static class ExtensionMethods
{
public static ICollection<SomeItem> OnlyVisible(this ICollection<SomeItem) items) {
return items.Where(e => e.isVisible).ToList();
}
}
Note that Where returns an IEnumerable, which you cannot modify, so I call ToList() which essentially does everything in your last sentence.
You would then use it like this:
void Foo(ICollection<SomeItem> items) {
foreach (var i in items.OnlyVisible()) {
// Use i
}
}
Try:
items.Where(e => e.isVisible == true).ToList()
I've written an extension method to add items to an (EF) EntityCollection. I got an interesting error, saying that my collection of IEnumerable ("items") had been modified, after the first loop in the foreach. When I turn items into items.ToList() (like in the code below), it works fine.
I completely understand that doing ToList() will produce a copy of the items on which the foreach will then operate.
What I do NOT understand is what is modifying the IEnumerable when I'm doing a foreach over it.
Update: Somehow, it seems the items variable is the same as the collections variable?
Update 2: I think collection and entity may be affected by EF's entity tracking, but I still fail to understand why
Usage:
ssp.ServiceAreas.ReplaceCollection(model.ServiceAreas);
Here's my extension method:
public static void AddOrUpdate<TEntity>(this EntityCollection<TEntity> collection, IEnumerable<TEntity> items)
where TEntity : EntityObject, IProjectIdentity<int>, new()
{
foreach (var item in items.ToList())
collection.AddOrUpdate(item);
}
public static void AddOrUpdate<TEntity>(this EntityCollection<TEntity> collection, TEntity item)
where TEntity : EntityObject, IProjectIdentity<int>, new()
{
if (item.ID > 0 && collection.Any(c => c.ID == item.ID))
collection.Remove(collection.First(c => c.ID == item.ID));
// For now, the Remove NEVER gets hit
collection.Add(item);
}
collection.Remove(collection.First(c => c.ID == item.ID));
you are removing in the collection you are iterating.
I created the following sample code:
internal class Program
{
private static void Main(string[] args)
{
var one = new List<string> {"Adam", "John"};
var two = new List<string> {"Adam", "Houldsworth"};
one.AddOrUpdate(two);
Console.Read();
}
}
static class Extensions
{
public static void AddOrUpdate(this IList<string> collection, IEnumerable<string> items)
{
foreach (var item in items.ToList())
collection.AddOrUpdate2(item);
}
public static void AddOrUpdate2(this IList<string> collection, string item)
{
if (collection.Any(c => c == item))
collection.Remove(collection.First(c => c == item));
collection.Add(item);
}
}
This works as you would expect, there are no errors. So in essence, none of the lines are causing issues.
What will cause issues is if you call the list on itself:
one.AddOrUpdate(one);
So from what I can see, you must be calling this extension method with the same collection as both arguments.
If you are, then both Remove or Add will mutate the collection and cause this exception.
Perhaps EntityCollection doesn't like when someone takes over it's elements? So, when you Add to collection, item gets Removed from items.
Or it could be that items == collection
It could be that the first item is always a new item, and thus by default the ID is set to some initial value. It will then be added to the collection, letting the EntityFramework generate a new ID and assign it to the first added item.
It then might be that the EntityCollection thinks it has changed, because it uses the ID to sort or do something else internally. And thus the foreach operation (which is probably using the same list) throws the exception. That's also why the test-case proved by Adam Houldsworth does not give the issue!
EntityCollection<Customer> customers = new EntityCollection<Customer>();
Customer newCustomer = new Customer() {ID = 0};
customers.Add(newCustomer);
customers.AddOrUpdate(customers);