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.
Related
return ship.DefenseType?.PropulsionMethod != null
? new BattleMethod(ship.DefenseType.PropulsionMethod)
: null;
Hi, my current return statement, above, is returning a Propulsion method if it's not null. However, my database has different types of
propulsion methods denoted by the first 2 letters in the field(PK, PA, PT, etc).
How can I check to make sure that the PropulsionMethod starts with "PK" before going further into the return statement?
In pseudo code, it might look something like this:
if (ship.DefenseType?.PropulsionMethod).startsWith("PK")
&& ship.DefenseType?.PropulsionMethod != null)
{
return new BattleMethod(ship.DefenseType.PropulsionMethod)
}
else
{
return null;
}
I tried
return ship.DefenseType?.PropulsionMethod != null &&
ship.DefenseType?.PropulsionMethod.StartsWith("PK")
? new BattleMethod(ship.DefenseType.PropulsionMethod)
: null;
But I get this error:
operator && cannot be applied to operands of type bool and bool?
Just add this condition too:
return ship.DefenseType?.PropulsionMethod != null
&& ship.DefenseType?.PropulsionMethod.StartsWith("PK")
? new BattleMethod(ship.DefenseType.PropulsionMethod) : null;
As the operator is && so the second condition will be evaluated if the first one was true (not null in this case).
You can compare nullable bool directly with true:
return ship.DefenseType?.PropulsionMethod?.StartsWith("PK") == true
? new BattleMethod(ship.DefenseType.PropulsionMethod)
: null;
I was just looking at the Guidelines for Overloading Equals() on msdn (see code below); most of it is clear to me, but there is one line I don't get.
if ((System.Object)p == null)
or, in the second override
if ((object)p == null)
Why not simply
if (p == null)
What is the cast to object buying us?
public override bool Equals(System.Object obj)
{
// If parameter is null return false.
if (obj == null)
{
return false;
}
// If parameter cannot be cast to Point return false.
TwoDPoint p = obj as TwoDPoint;
if ((System.Object)p == null)
{
return false;
}
// Return true if the fields match:
return (x == p.x) && (y == p.y);
}
public bool Equals(TwoDPoint p)
{
// If parameter is null return false:
if ((object)p == null)
{
return false;
}
// Return true if the fields match:
return (x == p.x) && (y == p.y);
}
The == operator may be overridden, and if it is, the default reference comparison may not be what you get. Casting to System.Object ensures that calling == performs a reference equality test.
public static bool operator ==(MyObj a, MyObj b)
{
// don't do this!
return true;
}
...
MyObj a = new MyObj();
MyObj b = null;
Console.WriteLine(a == b); // prints true
Console.WriteLine((object)a == (object)b); // prints false
I prefer using object.ReferenceEquals(a, b) in this ambiguous context to force reference comparison because it makes the intent clear while preserving the semantics precisely (in fact, ReferenceEquals is implemented like that).
I suppose, since the article also talks about overriding operator==, that it's forcing it to use the == operator defined on Object rather than any overloaded operator in the current class.
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) => ...))
I'm working with the new Task Parallel Library and today went to this case:
This code doesn't compile:
internal Task<Guid?> SavePages(string[] pages)
{
return Task.Run(() =>
{
if (pages == null || pages.Length == 0)
return null;
....
Unless I explicitly returns a null nullable Guid:
internal Task<Guid?> SavePages(string[] pages)
{
return Task.Run(() =>
{
if (pages == null || pages.Length == 0)
return (Guid?)null;
// Check documents path access
Why this behavior, I'm I doing something wrong? I mean, I get the code to work with the second option but don't know If I'm misusing the library, I mean, null is always null, isn't it?
Compile error:
Cannot convert lambda expression to delegate type
'System.Func' because some of the return
types in the block are not implicitly convertible to the delegate
return type
http://msdn.microsoft.com/en-us/library/dd460717.aspx
This has to do with the way the compiler determines the type of your lambda. When you return a plain null, the only thing the compiler can imply is that you are returning an object. Hence, your parameterless lambda is compatible with Task<object>. However, the signature of your function says that you are returning Task<Guid?>, so the return type the compiler implied from your code is not compatible. When you cast that null to Guid?, you provide the compiler a the clue it is missing to make the lambda a Task<Guid?>.
This is a limitation with type inference in the C# compiler. This issue is not unrelated to the one involving the ternary operator:
int? num = a != null ? a.Value : null; // Will not compile
int? num = a != null ? a.Value : (int?)null; // Compiles
int? num = a != null ? (int?)a.Value : null; // Compiles
Another workaround for your specific situation is to specify the generic type explicitly:
return Task.Run<Guid?>(() =>
{
if (pages == null || pages.Length == 0)
return null;
I was just looking at the Guidelines for Overloading Equals() on msdn (see code below); most of it is clear to me, but there is one line I don't get.
if ((System.Object)p == null)
or, in the second override
if ((object)p == null)
Why not simply
if (p == null)
What is the cast to object buying us?
public override bool Equals(System.Object obj)
{
// If parameter is null return false.
if (obj == null)
{
return false;
}
// If parameter cannot be cast to Point return false.
TwoDPoint p = obj as TwoDPoint;
if ((System.Object)p == null)
{
return false;
}
// Return true if the fields match:
return (x == p.x) && (y == p.y);
}
public bool Equals(TwoDPoint p)
{
// If parameter is null return false:
if ((object)p == null)
{
return false;
}
// Return true if the fields match:
return (x == p.x) && (y == p.y);
}
The == operator may be overridden, and if it is, the default reference comparison may not be what you get. Casting to System.Object ensures that calling == performs a reference equality test.
public static bool operator ==(MyObj a, MyObj b)
{
// don't do this!
return true;
}
...
MyObj a = new MyObj();
MyObj b = null;
Console.WriteLine(a == b); // prints true
Console.WriteLine((object)a == (object)b); // prints false
I prefer using object.ReferenceEquals(a, b) in this ambiguous context to force reference comparison because it makes the intent clear while preserving the semantics precisely (in fact, ReferenceEquals is implemented like that).
I suppose, since the article also talks about overriding operator==, that it's forcing it to use the == operator defined on Object rather than any overloaded operator in the current class.