LINQ where doesn't accept 1 or 2 parameters - c#

The following code with a boolean parameter works pretty well:
public List<T> SearchByStatus(bool status, List<T> list)
{
return (List<T>)list.Where(_item => _item.Executed == status);
}
But if I want to use something like this
public List<T> SearchByCodeType(ECodes codeType, List<T> list)
{
return (List<T>)list.Where(_item => _item.CodeType == codeType);
}
, the IDE throws an error saying Func<T, int, bool> doesn't accept 1 parameter.
I researched a bit and found for example this.
If I now add a seond parameter, lets say
public List<T> SearchByCodeType(ECodes codeType, List<T> list)
{
return (List<T>)list.Where((_item, _index) => _item.CodeType == codeType);
}
it says Func<T, bool> doens't accept 2 parameters.
The messages itself are correct, but I don't get why it assumes I want to use the overloaded version of Where in the first case and the non-overloaded in the second... Am I doing something wrong?
P.S.: The ECodes-type used is defined as
public enum ECodes : int
{
....
}
May that cause the issue?

Both of these should work fine:
public List<T> SearchByCodeType(ECodes codeType, List<T> list)
{
return list.Where((_item, _index) => _item.CodeType == codeType).ToList();
}
public List<T> SearchByCodeType(ECodes codeType, List<T> list)
{
return list.Where(_item => _item.CodeType == codeType).ToList();
}
If they don't - please check whether you have using System.Linq; at the top, and are using regular LINQ (not something obscure like LINQBridge).
You could also use:
public List<T> SearchByCodeType(ECodes codeType, List<T> list)
{
return list.FindAll(_item => _item.CodeType == codeType);
}
Note that all of this assumes that you have a suitable generic constraint on T such that T.CodeType is well-defined - presumably:
class Foo<T> where T : IHazCodeType
{
List<T> SearchByCodeType(ECodes codeType, List<T> list) {...}
}
interface IHazCodeType
{
ECodes CodeType {get;}
}

Related

What Type to use for Generic IList Method

I have tried to write a method that converts any IList of things into a comma-separated list of those things as a string. It looks like this:
public static string ToStringList<T>(this T source) where T : IList<object>
{
string list_string = "[EMPTY]";
try
{
if (source != null && source.Count() > 0)
{
list_string = "";
foreach (var item in source)
{
//ToString unnecessarily written here to highlight the usage
list_string += $", {item.ToString()}";
}
}
}
catch
{
list_string = "[ERROR - could not list values]";
}
list_string = list_string.StartsWith(", ") ? list_string.Substring(2) : list_string;
return list_string;
}
I would like to use this method on an Observable collection of Sites:
public class Site
{
public string Name { get; set; }
public string code { get; set; }
public override string ToString()
{
return Name;
}
}
however, when I try to run the following code, I get a conversion error:
public ObservableCollection<Site> SelectedSites {get;set;}
//[some skipped code that inserts values into the ObservableCollection]
//Error: Cannot convert from Site to object
var sites = SelectedSites.ToStringList();
I understand why I get the conversion error - there is no way for the code to know how to convert the Site into an object. However, given that ToString() exists on everything, is there a way that I can alter the method ToStringList() such that it can accept an IList of any type?
I have read some articles and pages about IList (like this and this), but honestly they have as much confused as enlightened - is that because what I ask is impossible or so long winded as to be impractical (in which case I can find another way)?
I am using .NET Framework 4.8.
Your extension method isn't available on an ObservableCollection<Site>, because an IList<Site> isn't related to IList<object> at all (See here for why).
You can instead use IList<T> as the parameter type:
public static string ToStringList<T>(this IList<T> source)
Now this will be available on ObservableCollection<Site>, because it implements IList<Site>, and the compiler can infer that T is Site.
Since you are not using any of the specific things that IList<T> provides, you can also define this method for the more general IEnumerable<T>. However, calling Count() on a general IEnumerable could be an O(n) operation. You might want to use Any() to check if there is any elements instead.
public static string ToStringList<T>(this IEnumerable<T> source)
Also note that you seem to be reinventing string.Join a little bit:
public static string ToStringList<T>(this IEnumerable<T> source)
{
try
{
const string empty = "[EMPTY]";
if (source != null)
{
return string.Join(", ", source.Select(x => x.ToString()).DefaultIfEmpty(empty));
}
else
{
return empty;
}
}
catch
{
return "[ERROR - could not list values]";
}
}
Change
public static string ToStringList<T>(this T source) where T : IList<object>
To
public static string ToStringList<T>(this IList<T> source) where T : class

LINQ: Customize the count method

I'm trying to implement a custom LinQ Count() method. Basically what I'm trying to achieve here is before calling the Count method, I want to filter out all elements that have the property IsDeleted set to true. So, I created an extension class and I added these methods:
public static int Count2<T>(this IEnumerable<T> source, Func<T, bool> selector)
where T : Model
{
return source.Where(x => !x.IsDeleted).Count(selector);
}
public static int Count2<T>(this IQueryable<T> source, Expression<Func<T, bool>> selector)
where T : Model
{
return source.Where(x => !x.IsDeleted).Count(selector);
}
public static int Count2<T>(this IEnumerable<T> source)
where T : Model
{
return source.Count(x => !x.IsDeleted);
}
public static int Count2<T>(this IQueryable<T> source)
where T : Model
{
return source.Count(x => !x.IsDeleted);
}
This works just find for local collections, but when executing this command for instance:
ListOfModels.Sum(x => x.PropertyThatIsAList.Count2())
and ListOfModels is an instance of IQueryable, i.e. it has to be executed in the database, it gives me this error:
The LINQ expression 'Sum()' could not be translated and will be evaluated locally.
I looked around on the web and I saw some answers saying I have to implement the IQueryableProvider but I think there is no need to go into such complicated path since the Sum() and Count() are translatable, I only need to count conditionally. Is it possible, and if it is, can anyone give me a clue on how to do it?
I suggest you instead of customizing all LinQ methods use an extended method like Validate():
public static IEnumerable<T> Validate<T>(this IEnumerable<T> list) where T: IDeleteable
{
return list.Where(w => !w.IsDeleted);
}
That IDeleteable interface is like this:
public interface IDeleteable
{
bool IsDeleted { get; set; }
}
Then use it before other methods.

Creating generic method

Can someone please help with the following... it's driving me nuts...
// Three methods, virtually identical with the exception of the select field
public IEnumerable<int> GetBrandID()
{
return myData.Select(m => m.BrandID).Distinct();
}
public IEnumerable<int> GetModelID()
{
return myData.Select(m => m.ModelID).Distinct();
}
public IEnumerable<int> GetVehicleID()
{
return myData.Select(m => m.VehicleID).Distinct();
}
// How to create one method which returns the type specified in the parameter??
public IEnumerable<int> GetData(??? myType)
{
return myData.Select(m => myType).Distinct();
}
It sounds like you probably just want a Func<Model, int> parameter:
public IEnumerable<int> GetData(Func<Model, int> projection)
{
return myData.Select(projection).Distinct();
}
You could then have:
var modelIds = GetData(m => m.ModelID);
var vehicleIds = GetData(m => m.VehicleID);
Is that what you're after? (That's assuming myData is an IEnumerable<Model>. If it's an IQueryable<Model> you may want to accept Expression<Func<Model, int>> instead.)
It's not clear what you're exactly after. Maybe something like this?
public static IEnumerable<TResult> GetData<TModel, TResult> (this IEnumerable<TModel> enumerable, Func<TModel, TResult> projection)
{
return enumerable.Select(projection);
}
And than just call like this:
var ints = myData.GetData<MyModel,int>(m=>m.ModelID).Distinct();
var doubles = myData.GetData<MyModel,double>(m=>m.DoubleProp).Distinct();
etc...
public ICollection<T> FindAllWhere(Expression<Func<T, bool>> selectSelector)
{
ICollection<T> elements = EntitySet.Select(selectSelector).ToList().Distinct();
return elements;
}

defining a generic action that takes a generic action as parameter

I am using .Net 4.5. How do I define a generci action that takes in another generic action as a parameter?
When I try the following, the compiler seems to interpret T as an actual type and complains that its definitions is not found:
public static Action<Action<T>, IEnumerable<T>> RepeatForEach = (a, x) =>
{
foreach (T t in x)
{
a.Invoke(t);
}
};
I tried the following, none of which worked:
Action<T><Action<T>>
Action<Action<T>><T>
Action<Action<T>> where T : object
I dont think it is possible to create a generic type based upon another generic. As said by sstan:
"Even generic types need to be declared somewhere."
Instead you should try another approach, what about you create an extention method instead? This is my sugestion:
public static void RepeatForEach(this Action<T> action, IEnumerable<T> itens)
{
foreach (T t in x)
{
a.Invoke(t);
}
}
So you can call it by:
Action<int> t = (int i)=> Console.WriteLine(i);
t.RepeatForEach(myitens)
I'm afraid that C# doesn't support this level of genericity. You will have to make the enclosing scope generic.
Either have a class:
public static class RepeatForEach<T>
{
public static Action<Action<T>, IEnumerable<T>> Action = (action, enumerable)
{
foreach (var element in enumerable)
{
action.Invoke(element);
}
}
}
Or have a method returning the action:
public static class MyClass
{
public static Action<Action<T>, IEnumerable<T>> GetRepeatForEachAction<T>()
{
return (action, enumerable) =>
{
foreach (var element in enumerable)
{
action.Invoke(element);
}
}
}
}
I'd also like to put forth an idea that having something do foreach on IEnumerable is generally a very bad idea. The reason for that would be to achieve side effects and IEnumerable is not built for that.
Imagine having an IEnumerable created via yield return. Then your action might not affect any of the elements (as those may be thrown away and recreated upon enumeration).

Calling a Delegate of an instance method on a Generic

I'm not having much luck searching for this answer, as I think that I don't know enough about the proper terms for it.
(Edit for clarity of code and how I call it)
I have a class that instantiates an extension method:
public static class Foo
{
public static IList<T> Bar<T>(this DataTable table) where T : class, new()
{
IList<T> list = ... // do something with DataTable.
return list;
}
}
I call this method like this:
DataTable table = SomehowGetTable();
IList<MyObject> list = table.Bar<MyObject>();
Again, greatly simplified.
Now, what I'd like to do, is add a delegate(?) so that after I get the list, I can call a method on each T in the list, like so:
public static class Foo
{
public static IList<T> Bar<T>(this DataTable table, MyDelegate postProcess)
{
IList<T> list = ... // do something with DataTable.
foreach(T item in list)
{
t.postProcess();
}
return list;
}
}
I don't know what I need to be able to pass in postProcess.. a delegate, a Predicate, or an Action, mostly because I'm Linq-challenged still.
Or, maybe this isn't even possible?
What you want is an Action< T > so you can do:
public static class Foo
{
public static IList<T> Bar(this DataTable table, Action<T> postProcess)
{
IList<T> list = ... // do something with DataTable.
foreach(T item in list)
{
postProcess(item);
}
return list;
}
}
First, you need to define Bar as generic, otherwise it won't compile.
Second, if you're trying to operate on each element within the list, you need to pass in a delegate that takes a single parameter of type T and returns no value. A built-in .NET delegate type is Action<T>, and it'll do fine.
So,
public static IList<T> Bar<T>(this DataTable table, Action<T> postProcess)
{
IList<T> list = ... // do something with DataTable
foreach(T item in list)
{
postProcess(item);
}
return list;
}
The signature would be
public static IList<T> Bar<T>(this DataTable table, Action<T> postProcess) {
var list = // Get ILIst<T> from DataTable
foreach (var i in list)
postProcess(i);
}
These days .Net brings virtually all method signatures to the table that you may ever need through Action and Func delegates. While action covers all void return type methods, Func introduces non-void returns.
Note that T must be defined as type argument on your method. The compiler may be able to infer T from the action you provide into the method:
List<double> myDoubles = table.Bar((double x) => Debug.Writeline(x));
For example if you are actually processing the values coming into a different type, the signature may look like :
public static IList<Z> Bar<T,Z>(this DataTable table, Func<T,Z> postProcess) {
return /* Get Listof T */ .Select(postProcess).ToList();
}
Used like
List<int> values = table.Bar((double d) => (int)d);
You can do either
public static IList<T> Bar<T>(this DataTable table, Action<T> postProcess)
{
...
postProcess(someT);
...
}
or add a generic constraint:
public static IList<T> Bar<T>(this DataTable table)
where T : IHasPostProcess
{
...
someT.postProcess();
...
}
Try Action<T>:
public static class Foo
{
public static IList<T> Bar(this DataTable table, Action<T> postProcess)
{
IList<T> list = ... // do something with DataTable.
foreach(T item in list)
{
postProcess(item);
}
return list;
}
}
Or use an interface so you do not have to pass action: (I prefer this one)
public interface IDo
{
Do();
}
public static class Foo
{
public static IList<T> Bar(this DataTable table) where T : IDo
{
IList<T> list = ... // do something with DataTable.
foreach(T item in list)
{
item.Do();
}
return list;
}
}

Categories