Here's two simple classes:
class Abc { public int x; }
class Bcd { public int y; }
Given that obj is of type object, here's a couple of examples of testing for Abc or Bcd with certain characteristics:
if (obj is Abc && (obj as Abc).x > 0 && Math.Pow((obj as Abc).x, 2) > 100)
Console.WriteLine(123);
if (obj is Bcd && (obj as Bcd).y > 0 && Math.Pow((obj as Bcd).y, 2) > 100)
Console.WriteLine(234);
What's a good way to deal with this pattern of:
if (obj is Abc && (obj as Abc).SomePropertyOrMethod && ...
One approach is an Is extension method:
public static bool Is<T>(this object obj, Func<T, bool> pred) where T : class
=> (obj is T) && pred(obj as T);
With this, the above examples can be written as:
if (obj.Is<Abc>(abc => abc.x > 0 && Math.Pow(abc.x, 2) > 100))
Console.WriteLine(123);
if (obj.Is<Bcd>(bcd => bcd.y > 0 && Math.Pow(bcd.y, 2) > 100))
Console.WriteLine(234);
In this way the (obj as ...) expressions are not repeated.
Are there other approaches to this pattern?
This pattern matching proposal seems like it would handle this well (see section 8.1).
I feel this takes a simple thing and makes it complicated. Also the namespace pollution resulting from extending object isn't nice. Further, this is a misuse of as. This should be a throwing cast (e.g. ((Abc)obj)) because we expect the cast to always succeed. A throwing cast has an assertion and a nice exception for that built-in. It's documenting the fact that this is expected to succeed.
An alternative would be to declare a variable:
var abc = obj as Abc;
if (abc != null && abc.x > 0 && Math.Pow(abc.x, 2) > 100)
Console.WriteLine(123);
Seems simple enough. I see no problems with that.
That said the extension method approach can be useful in places where you cannot easly declare a variable, for example in certain queries or in deeply nested expressions. That's generally undesirable but it sometimes happens.
I am not sure what's best/worst but (obj as Abc).x may end up in NullReferenceException if the cast fails. One way I see is breaking down the condition check like below:
Abc a = obj as Abc;
if (a != null && a.x > 0 && Math.Pow(a.x, 2) > 100)
Console.WriteLine(123);
That way, no need of checking the condition obj is Abc since a != null will be true only when obj as Abc casting successes.
Add some static checking:
public static bool Is<T, TBase>(this TBase obj, Func<T, bool> pred)
where TBase : class
where T : class, TBase
{
var t = obj as T;
return t != null && pred(t);
}
Use like this:
TypeBase obj;
...
if (obj.Is((TypeA obj) => ...))
Related
I am building a method that takes a List<T> and turns it into a DataTabe. In the process, I want to filter out any properties that are tagged with the [NotMapped] attribute.
I have the entire method working, but I am a bit worried about one part of it... the part that weeds out the [NotMapped] properties. Here's what I've got:
public static DataTable CreateDataTable<T>(IEnumerable<T> list)
{
Type type = typeof(T);
var properties = type.GetProperties().Where(p =>
p.CustomAttributes.ToList().Count == 0 ||
(p.CustomAttributes.ToList().Count > 0 && p.CustomAttributes.ToList()[0].AttributeType.Name != "NotMappedAttribute")
).ToList();
// Rest of the method...
}
So, that works as I'd like it to and gets rid of anything that looks like this (for example) so it doesn't end up in the final DataTable:
[NotMapped]
public string Description { get; set; }
My concern is about performance and just general best practice. The var properties = LINQ query seems clumsy to me, but I'm not seeing a more efficient way to improve it.
Namely, I don't like calling p.CustomAttributes.ToList() 3 times. Is there a way to avoid this?
private IEnumerable<PropertyInfo> GetPropertiesWithoutAttribute<TAttribute>(Type type)
where TAttribute : Attribute
{
return type.GetProperties().Where(p => !p.GetCustomAttributes<TAttribute>().Any());
}
To answer the original question, you can avoid calling ToList() multiple times by saving its return value:
type.GetProperties().Where(p =>
p.CustomAttributes.ToList().Count == 0 ||
(p.CustomAttributes.ToList().Count > 0 && p.CustomAttributes.ToList()[0].AttributeType.Name != "NotMappedAttribute")
)
... becomes...
type.GetProperties().Where(p =>
{
var attrs = p.CustomAttributes.List();
return attrs.Count == 0 || (attrs.Count > 0 && attrs[0].AttributeType.Name != "NotMappedAttribute");
})
However, I recommend instead doing this:
type.GetProperties().Where(p => p.GetCustomAttribute<NotMappedAttribute>() == null))
Its shorter and easier to understand at a glance.
My code below gives me a NullReferenceException and the stack trace tells me the problem is in the Count method, so I'm pretty sure at some point foo, bar or baz is null.
My code:
IQueryable<IGrouping<string, Referral>> queryable= ...;
var dict = queryable.ToDictionary(g => g.Key.ToString(),
g => g.Count(r => r.foo.bar.baz.dummy == "Success"));
I'm wondering what's a concise way to handle null cases.
I learn that in C# 6.0 I can just do foo?.bar?.baz?.dummy, however the project I'm working on isn't C# 6.0
A solution for <6.0 would be:
.Count(r => r.foo != null &&
r.foo.bar != null &&
r.foo.bar.baz != null &&
r.foo.bar.baz.dummy == "Success")
Exactly for complex constructs like the one above the null propagation operator was introduced.
Furthermore you could also refactor the expression into a private method:
private Expression<Func<Referral, bool>> Filter(string value)
{
return r => r.foo != null &&
r.foo.bar != null &&
r.foo.bar.baz != null &&
r.foo.bar.baz.dummy == value;
}
and use it as follows:
g => g.Count(Filter("Success"))
You can use the following extension methods.
public static TResult With<TInput, TResult>(this TInput o, Func<TInput, TResult> evaluator)
where TResult : class
where TInput : class
{
return o == null ? null : evaluator(o);
}
public static TResult Return<TInput, TResult>(this TInput o, Func<TInput, TResult> evaluator, TResult failureValue)
where TInput : class
{
return o == null ? failureValue : evaluator(o);
}
Their combination gives you a nice, readable API for handling nulls:
return foo
.With(o => o.bar)
.With(o => o.baz)
.Return(o => o.dummy, null);
The problem is that the ToDictionary method isn't actually done on the queryable - instead, you get the whole collection, and do the aggregation in your application, rather than on the DB server.
So instead of using ToDictionary directly, use Select first:
IQueryable<IGrouping<string, Referral>> queryable= ...;
var dict = queryable.Select(g => new { Key = g.Key.ToString(),
Count = g.Count(r => r.foo.bar.baz.dummy == "Success") })
.ToDictionary(i => i.Key, i => i.Count);
This will make sure the aggregation is done in the database (where you don't care about those nulls) and not in the C# code (where you get a NullReferenceException).
Of course, this assumes that the queryable you're using is a DB query (or, to be more precise, a queryable that supports aggregation and has ANSI SQL-like NULL semantics). If you have a different custom queryable, it's not going to help (unless you explicitly add those NULL semantics yourself).
// if null, use null
if(objectvariable == null)
{
// throw exception
}
// if not null
if(objectvariable != null)
{
// continue
}
I have asked this
the problem was solved and Works with one condition, but if I try this:
myOC.Insert(myNewElement, (x=> x.ID != 1 && x.Name.CompareTo(myString) > 0))
In this case I get an error that says that the delegate Function does not take 1 argument.
My Extension method is:
public static void Insert<T>(this ObservableCollection<T> paramOC, T paramNewElement, Func<T, bool> condition)
{
//Code
}
How can I set many conditions in the funcion?
thank you so much.
Try to use this
myOC.Insert(myNewElement, (x=> x.ID != 1 && (x.Name.CompareTo(myString) > 0)))
I have two conditions, they may be both set or just one of them. If both set to a value a linq where statement shall be triggered having AND as boolean operator else the linq where statement shall include only the filter based on the set condition.
For example:
both conditions set: linq.Where(condition1 && condition2)...
only condition1 set: linq.Where(condition1)..
only condition2 set: linq.Where(condition2)..
This is not allowed: linq.Where(condition1 || condition2) because the filtering shall be very accurate
Now my problem is how do I write ONE linq statement that considers all those requirements? Thanks in advance
EDIT:
Predicates might look like this: x => x > 10 or x => x == "Hello" or x => x + 2 == 3.. any expression that fits inside where statement.
I get the conditions/predicates, i do not create them myself
With predicates i mean those predicates in math and not the class in c#.
there is not much code to post since i do not know how to do this in ONE linq statement
The result has to be ONE linq statement handling all this.
I am new I do not know how to write this that why i was hoping you could help me out.
If you dont like it dont downvote it just pass on, somebody will help me out.
EDIT2:
I mean this. Not more, not less:
method<T> filterList(ienumerable<T> linq, Predicate<T> p1, Predicate<T> p2)
{
return linq.Where(only p1 or only p2 or p1 && p2)
}
This is the beauty of monads - you don't have to do everything in one step.
// bool? flag1
// bool? flag2
// IQueryable<something> collection
if(flag1.HasValue)
collection = collection.Where(x => x.Flag1 == flag1);
if(flag2.HasValue)
collection = collection.Where(x => x.Flag2 == flag2);
//...
Edit: if you want to do it in a single LINQ statement you'll need to do some trickery by invoking predicates:
public IEnumerable<T> FilterList<T>(IEnumerable<T> collection, Predicate<T> p1, Predicate<T> p2)
{
return collection.Where(x =>
(p1 == null || p1.Invoke(x)) &&
(p2 == null || p2.Invoke(x))
);
}
you don't say what to do if both are unset, I assume in this case no filtering at all
I assume by 'has no value' you mean for example p1 is null.
I'm not 100% sure what you mean by ONE linq statement but I would assume at least one of the below would meet your needs.
in all cases I'm saying basically [replace a null predicate with an always true predicate and and the predicates]
Solution 1
return linq.Where(x => p1 != null ? p1(x) : true).Where(x => p2 != null ? p2(x) : true);
Solution 2
var p1a = p1 ?? (x => true);
var p2a = p2 ?? (x => true);
return linq.Where(x => p1a(x) && p2a(x));
Solution 3
return linq.Where(x => (p1 ?? (y=>true))(x) && (p2 ?? (z=>true))(x));
If you use the third party PredicateBuilder, you can iteratively build or filters as well as and.
example
var predicate = PredicateBuilder.False<Product>();
foreach (string keyword in keywords)
{
string temp = keyword;
predicate = predicate.Or (p => p.Description.Contains (temp));
}
return dataContext.Products.Where (predicate);
In some C# code, I use linq GroupBy<TSource, TKey>() method with a custom IEqualityComparer<T>.
GroupBy(x => x.SomeField, new FooComparer());
The field i use as a grouping key can be null. As a consequence, i had to add some null checks in Equals() method :
public bool Equals(Foo x, Foo y)
{
if (x == null && y == null)
return true;
else if (x == null && y != null)
return false;
else if (x != null && y == null)
return false;
else
return x.Id == y.Id;
}
The question is : should I do the same in GetHashCode() function ?
public int GetHashCode(Foo obj)
{
if (obj == null) //is this really needed ?
return default(int); //
else
return obj.Id;
}
Something i do not understand : even with null keys provided in GroupBy() method, GetHashCode() is never called with a null object in obj parameter. Can somebody explain me why ? (is it just "pure chance" because the way GroupBy() is implemented and order of the elements i give to it ?)
EDIT :
as caerolus pointed it out, there is some special checks made in GroupBy() implementation.
I checked in ILSpy and GroupBy() is implemented with a Lookup<TKey, TElement>
Here is the revelant function :
internal int InternalGetHashCode(TKey key)
{
if (key != null)
{
return this.comparer.GetHashCode(key) & 2147483647;
}
return 0;
}
According to the documentation of IEqualityComparer<T>.GetHashCode:
ArgumentNullException
The type of obj is a reference type and obj is null.
So this is part of the contract of that interface, and as such you should care. Implement it by throwing ArgumentNullException if obj is null.
You should always adhere to an interface, even if you suspect or can prove that the code will never touch the parts you don't care about. Changes later might introduce code that relies on that behaviour.