Linq .SingleOrDefault - how to setup a default for a custom class? - c#

i went over some questions and searched google a bit,
but i couldnt find an answer ( That satisfies me ).
Basicly, i understand the SingleOrDefault return null or 0 ( depends on the type ).
but how can i make it return something else ?
return myChannels.All.Where(_Channel => _Channel.Guid == this.ParentChannelGuid).SingleOrDefault(_SPECIFICCHANNEL);
so, i want _SPECIFICCHANNEL to be returned in case it is not single..
can that be done ?

You will have to make an extension method:
public static T SingleOr<T>(this IEnumerable<T> list, T defaultValue) where T : class
{
return list.SingleOrDefault() ?? defaultValue;
}
There is no other way. All classes default to null.

This can be accomplished in a rather simple way. If you create your own extension method that is more specific than the generic SingleOrDefault, then the compiler will prefer the more type-specific version. Here's an example that shows how to do that with a simple Person class (you can copy-paste it into LINQPad to quickly see the result):
public class Person
{
public string Name { get; set; }
public override string ToString()
{
return Name ?? "";
}
}
public static class PersonExtensionMethod
{
public static Person SingleOrDefault(this IEnumerable<Person> source)
{
var person = Enumerable.SingleOrDefault(source);
if (person == null)
return new Person { Name = "Unnamed" };
return person;
}
}
public static void Main()
{
var emptyCollection = new Person[0];
var nonEmptyCollection = new Person[] { new Person { Name = "Jack" } };
Debug.WriteLine("Empty collection: " + emptyCollection.SingleOrDefault());
Debug.WriteLine("Non-empty collection: " + nonEmptyCollection.SingleOrDefault());
}
In the above example, SingleOrDefault(IEnumerable<Person>), takes precedence over SingleOrDefault<T>(IEnumerable<T>) which is less specific.

Could you use DefaultIfEmpty() (psedo code follows) -
return myChannels.All.Where(_Channel => _Channel.Guid == this.ParentChannelGuid).DefaultIfEmpty(_SPECIFICCHANNEL).SingleOrDefault();

but how can i make it return something else ?
You cannot. You can make your own method — as shown by Oskar Kjellin — to return someting else, but SingleOrDefault will always behave as programmed, which means return the default value (null, 0) for an item.

Why not just use the "??" operator and say
return myChannels.SingleOrDefault(_Channel => _Channel.Guid == this.ParentChannelGuid) ??_SPECIFICCHANNEL;

You cannot define the default value of a type. It is always defined as null for reference types. For structs, it is an instance of the struct where all member fields in turn are set to their default values. For enums, it is always 0 (which may or may not be a defined value of the enum type in question)

Expanding on Oskar's answer of an extension method you can make that more generic so that it covers value as well as reference types with something like the below:
public static T SingleOrSpecifiedDefault<T>(this IEnumerable<T> enumerable, Expression<Func<T, bool>> singleOrDefault, T defaultValue) where T : IComparable
{
T singleValue = enumerable.SingleOrDefault(singleOrDefault.Compile());
if (singleValue == null || singleValue.CompareTo(default(T)) == 0)
{
return defaultValue;
}
return singleValue;
}
This allows you to specify the same LINQ expression that you'd use with SingleOrDefault as well as the defaultValue (which will be used if no match is found).

Related

How to compare two IEnumerable<T> in C# if I don't know the actual object type?

I'm struggling with implementing the IEquatable<> interface for a class. The class has a Parameter property that uses a generic type. Basically the class definition is like this:
public class MyClass<T> : IEquatable<MyClass<T>>
{
public T Parameter { get; }
...
}
In the Equals() method I'm using EqualityComparer<T>.Default.Equals(Parameter, other.Parameter) to compare the property. Generally, this works fine – as long as the property is not a collection, for example an IEnumerable<T>. The problem is that the default equality comparer for IEnumerable<T> is checking reference equality.
Obviously, you'd want to use SequenceEqual() to compare the IEnumerable<T>. But to get this running, you need to specify the generic type of the SequenceEqual() method. This is the closest I could get:
var parameterType = typeof(T);
var enumerableType = parameterType.GetInterfaces()
.Where(type => type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>))
.Select(type => type.GetGenericArguments().First()).FirstOrDefault();
if (enumerableType != null)
{
var castedThis = Convert.ChangeType(Parameter, enumerableType);
var castedOther = Convert.ChangeType(other.Parameter, enumerableType);
var isEqual = castedThis.SequenceEqual(castedOther);
}
But this does not work because Convert.ChangeType() returns an object. And of course object does not implement SequenceEqual().
How do I get this working? Thanks for any tipps!
Best regards,
Oliver
Given that you have a generic container that you want to compare various generic items, you don't want to be hard coding in various specific equality checks for certain types. There are going to be lots of situations where the default equality comparison won't work for what some particular caller is trying to do. The comments have numerous different examples of problems that can come up, but also just consider the many many classes out there who's default equality is a reference comparison by for which someone might want a value comparison. You can't have this equality comparer just hard code in a solution for all of those types.
The solution of course is easy. Let the caller provide their own equality implementation, which in C#, means an IEqualityComparer<T>. Your class can become:
public class MyClass<T> : IEquatable<MyClass<T>>
{
private IEqualityComparer<T> comparer;
public MyClass(IEqualityComparer<T> innerComparer = null)
{
comparer = innerComparer ?? EqualityComparer<T>.Default;
}
public T Parameter { get; }
...
}
And now by default the default comparer will be used for any given type, but the caller can always specify a non-default comparer for any type that needs different equality semantics.
Effectively you want a way to say
var castedThis = (IEnumerable<U>)Convert.ChangeType(Parameter, enumerableType);
where T is IEnumerable<U> and U is dynamic.
I don't think you can do that.
If you are happy with some boxing though, you can use the non-generic IEnumerable interface:
public bool Equals(MyClass<T> other)
{
var parameterType = typeof(T);
if (typeof(IEnumerable).IsAssignableFrom(parameterType))
{
var castedThis = ((IEnumerable)this.Parameter).GetEnumerator();
var castedOther = ((IEnumerable)other.Parameter).GetEnumerator();
try
{
while (castedThis.MoveNext())
{
if (!castedOther.MoveNext())
return false;
if (!Convert.Equals(castedThis.Current, castedOther.Current))
return false;
}
return !castedOther.MoveNext();
}
finally
{
(castedThis as IDisposable)?.Dispose();
(castedOther as IDisposable)?.Dispose();
}
}
else
{
return EqualityComparer<T>.Default.Equals(this.Parameter, other.Parameter);
}
}
If you are not happy with the boxing, then you can use reflection to construct and call SequenceEqual (as inspired by How do I invoke an extension method using reflection?):
public bool Equals(MyClass<T> other)
{
var parameterType = typeof(T);
if (typeof(IEnumerable).IsAssignableFrom(parameterType))
{
var enumerableType = parameterType.GetGenericArguments().First();
var sequenceEqualMethod = typeof(Enumerable)
.GetMethods(BindingFlags.Static | BindingFlags.Public)
.Where(mi => {
if (mi.Name != "SequenceEqual")
return false;
if (mi.GetGenericArguments().Length != 1)
return false;
var pars = mi.GetParameters();
if (pars.Length != 2)
return false;
return pars[0].ParameterType.IsGenericType && pars[0].ParameterType.GetGenericTypeDefinition() == typeof(IEnumerable<>) && pars[1].ParameterType.IsGenericType && pars[1].ParameterType.GetGenericTypeDefinition() == typeof(IEnumerable<>);
})
.First()
.MakeGenericMethod(enumerableType)
;
return (bool)sequenceEqualMethod.Invoke(this.Parameter, new object[] { this.Parameter, other.Parameter });
}
else
{
return EqualityComparer<T>.Default.Equals(this.Parameter, other.Parameter);
}
}
You can cache the sequenceEqualMethod for better performance.

Converting a null type to a list (error handling)

So, the title is a bit misleading and I'll sort it out first.
Consider the following piece of code:
public static ADescription CreateDescription(string file, string name, params string[] othername)
{
return new ADescription(file, name, othername.ToList<string>());
}
This will throw a System.ArgumentNullException in a case where the user deliberately enters a null at the end. For e.g.:
ADescription.CreateDescription("file", "name", null); // example
Now I have a property that basically gets & sets the othername list. My concern is that I will have to check at every stage like (in the property, as well as in this method):
if (othername == null){
// do nothing
}
else{
othername.ToList<string>; // for example
}
because, null is acceptable for othername. Is there any way that c# natively provides this capability where if othername is null, then it wouldn't really operate ToList() on that.
You can use a ternary operator:
return new ADescription(file, name, othername==null?null:othername.ToList<string>());
Or create an extension method as described in the accepted response here Possible pitfalls of using this (extension method based) shorthand:
public static class IfNotNullExtensionMethod
{
public static U IfNotNull<T, U>(this T t, Func<T, U> fn)
{
return t != null ? fn(t) : default(U);
}
}
Your code would be:
return new ADescription(file, name, othername.IfNotNull(on => on.ToList());
You could make an extension method to handle this:
public static class MyExtensionMethods
{
public static List<T> ToListIfNotNull<T>(this IEnumerable<T> enumerable)
{
return (enumerable != null ? new List<T>(enumerable) : null);
}
}
Then you can substitute the extension method wherever you would otherwise use ToList().
return new ADescription(file, name, othername.ToListIfNotNull());

How to assert that two list contains elements with the same public properties in NUnit?

I want to assert that the elements of two list contains values that I expected, something like:
var foundCollection = fooManager.LoadFoo();
var expectedCollection = new List<Foo>()
{
new Foo() { Bar = "a", Bar2 = "b" },
new Foo() { Bar = "c", Bar2 = "d" }
};
//assert: I use AreEquivalent since the order does not matter
CollectionAssert.AreEquivalent(expectedCollection, foundCollection);
However the above code will not work (I guess because .Equals() does not return true for different objects with the same value). In my test, I only care about the public property values, not whether the objects are equal. What can I do to make my assertion?
REWORKED ANSWER
There is a CollectionAssert.AreEqual(IEnumerable, IEnumerable, IComparer) overload to assert that two collections contain the same objects in the same order, using an IComparer implementation to check the object equivalence.
In the scenario described above, the order is not important. However, to sufficiently handle also the situation where there are multiple equivalent objects in the two collections, it becomes necessary to first order the objects in each collection and use one-by-one comparison to ensure that also the number of equivalent objects are the same in the two collections.
Enumerable.OrderBy provides an overload that takes an IComparer<T> argument. To ensure that the two collections are sorted in the same order, it is more or less required that the types of the identifying properties implement IComparable. Here is an example of a comparer class that implements both the IComparer and IComparer<Foo> interfaces, and where it is assumed that Bar takes precedence when ordering:
public class FooComparer : IComparer, IComparer<Foo>
{
public int Compare(object x, object y)
{
var lhs = x as Foo;
var rhs = y as Foo;
if (lhs == null || rhs == null) throw new InvalidOperationException();
return Compare(lhs, rhs);
}
public int Compare(Foo x, Foo y)
{
int temp;
return (temp = x.Bar.CompareTo(y.Bar)) != 0 ? temp : x.Bar2.CompareTo(y.Bar2);
}
}
To assert that the objects in the two collections are the same and comes in equal numbers (but not necessarily in the same order to begin with), the following lines should do the trick:
var comparer = new FooComparer();
CollectionAssert.AreEqual(
expectedCollection.OrderBy(foo => foo, comparer),
foundCollection.OrderBy(foo => foo, comparer), comparer);
No, NUnit has no such mechanism as of current state. You'll have to roll your own assertion logic. Either as separate method, or utilizing Has.All.Matches:
Assert.That(found, Has.All.Matches<Foo>(f => IsInExpected(f, expected)));
private bool IsInExpected(Foo item, IEnumerable<Foo> expected)
{
var matchedItem = expected.FirstOrDefault(f =>
f.Bar1 == item.Bar1 &&
f.Bar2 == item.Bar2 &&
f.Bar3 == item.Bar3
);
return matchedItem != null;
}
This of course assumes you know all relevant properties upfront (otherwise, IsInExpected will have to resort to reflection) and that element order is not relevant.
(And your assumption was correct, NUnit's collection asserts use default comparers for types, which in most cases of user defined ones will be object's ReferenceEquals)
Using Has.All.Matches() works very well for comparing a found collection to the expected collection. However, it is not necessary to define the predicate used by Has.All.Matches() as a separate function. For relatively simple comparisons, the predicate can be included as part of the lambda expression like this.
Assert.That(found, Has.All.Matches<Foo>(f =>
expected.Any(e =>
f.Bar1 == e.Bar1 &&
f.Bar2 == e.Bar2 &&
f.Bar3 == e.Bar3)));
Now, while this assertion will ensure that every entry in the found collection also exists in the expected collection, it does not prove the reverse, namely that every entry in the expected collection is contained in the found collection. So, when it is important to know that found and expected contain are semantically equivalent (i.e., they contain the same semantically equivalent entries), we must add an additional assertion.
The simplest choice is to add the following.
Assert.AreEqual(found.Count(), expected.Count());
For those who prefer a bigger hammer, the following assertion could be used instead.
Assert.That(expected, Has.All.Matches<Foo>(e =>
found.Any(f =>
e.Bar1 == f.Bar1 &&
e.Bar2 == f.Bar2 &&
e.Bar3 == f.Bar3)));
By using the first assertion above in conjunction with either the second (preferred) or third assertion, we have now proven that the two collections are semantically the same.
Have you tried something like this?
Assert.That(expectedCollection, Is.EquivalentTo(foundCollection))
I had a similar problem. Listing contributors, which contains "commenters" and other ppl... I want to get all the comments and from that derive the creators, but I'm ofc only interested in unique creators. If someone created 50 comments I only want her name to appear once. So I write a test to see that the commenters are int the GetContributors() result.
I may be wrong, but what I think your after (what I was after when I found this post) is to assert that there are exactly one of each item in one collection, found in another collection.
I solved this like so:
Assert.IsTrue(commenters.All(c => actual.Count(p => p.Id == c.Id) == 1));
If you also want the resulting list not to contain other items than expected you could just compare the length of the lists as well..
Assert.IsTrue(commenters.length == actual.Count());
I hope this is helpful, if so, I'd be very grateful if you would rate my answer.
To perform equivilance operations on complex types you need to implement IComaprable.
http://support.microsoft.com/kb/320727
Alternatively you could use recursive reflection, which is less desirable.
One option is to write custom constraints to compare the items. Here's a nice article on the subject: http://www.davidarno.org/2012/07/25/improving-nunit-custom-constraints-with-syntax-helpers/
I recommend against using reflection or anything complex, it just adds more work/maintenace.
Serialize the object (i recommend json) and string compare them.
I'm unsure why you object to order by but I'd still recommend it as it will save a custom compare's for each type.
And it automatically works with domain objects change.
Example (SharpTestsEx for fluent)
using Newtonsoft.Json;
using SharpTestsEx;
JsonConvert.SerializeObject(actual).Should().Be.EqualTo(JsonConvert.SerializeObject(expected));
You can write it as a simple extensions and make it more readable.
public static class CollectionAssertExtensions
{
public static void CollectionAreEqual<T>(this IEnumerable<T> actual, IEnumerable<T> expected)
{
JsonConvert.SerializeObject(actual).Should().Be.EqualTo(JsonConvert.SerializeObject(expected));
}
}
and then using your example call it like so:
var foundCollection = fooManager.LoadFoo();
var expectedCollection = new List<Foo>()
{
new Foo() { Bar = "a", Bar2 = "b" },
new Foo() { Bar = "c", Bar2 = "d" }
};
foundCollection.CollectionAreEqual(foundCollection);
You'll get an assert message like so:
...:"a","Bar2":"b"},{"Bar":"d","Bar2":"d"}]
...:"a","Bar2":"b"},{"Bar":"c","Bar2":"d"}]
...__________________^_____
Simple code explaining how to use the IComparer
using System.Collections;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace CollectionAssert
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
IComparer collectionComparer = new CollectionComparer();
var expected = new List<SomeModel>{ new SomeModel { Name = "SomeOne", Age = 40}, new SomeModel{Name="SomeOther", Age = 50}};
var actual = new List<SomeModel> { new SomeModel { Name = "SomeOne", Age = 40 }, new SomeModel { Name = "SomeOther", Age = 50 } };
NUnit.Framework.CollectionAssert.AreEqual(expected, actual, collectionComparer);
}
}
public class SomeModel
{
public string Name { get; set; }
public int Age { get; set; }
}
public class CollectionComparer : IComparer, IComparer<SomeModel>
{
public int Compare(SomeModel x, SomeModel y)
{
if(x == null || y == null) return -1;
return x.Age == y.Age && x.Name == y.Name ? 0 : -1;
}
public int Compare(object x, object y)
{
var modelX = x as SomeModel;
var modelY = y as SomeModel;
return Compare(modelX, modelY);
}
}
}
This solved my problem using NUnit's Assertion class from the NUnitCore assembly:
AssertArrayEqualsByElements(list1.ToArray(), list2.ToArray());

List.Sort in C#: comparer being called with null object

I am getting strange behaviour using the built-in C# List.Sort function with a custom comparer.
For some reason it sometimes calls the comparer class's Compare method with a null object as one of the parameters. But if I check the list with the debugger there are no null objects in the collection.
My comparer class looks like this:
public class DelegateToComparer<T> : IComparer<T>
{
private readonly Func<T,T,int> _comparer;
public int Compare(T x, T y)
{
return _comparer(x, y);
}
public DelegateToComparer(Func<T, T, int> comparer)
{
_comparer = comparer;
}
}
This allows a delegate to be passed to the List.Sort method, like this:
mylist.Sort(new DelegateToComparer<MyClass>(
(x, y) => {
return x.SomeProp.CompareTo(y.SomeProp);
});
So the above delegate will throw a null reference exception for the x parameter, even though no elements of mylist are null.
UPDATE: Yes I am absolutely sure that it is parameter x throwing the null reference exception!
UPDATE: Instead of using the framework's List.Sort method, I tried a custom sort method (i.e. new BubbleSort().Sort(mylist)) and the problem went away. As I suspected, the List.Sort method passes null to the comparer for some reason.
This problem will occur when the comparison function is not consistent, such that x < y does not always imply y < x. In your example, you should check how two instances of the type of SomeProp are being compared.
Here's an example that reproduces the problem. Here, it's caused by the pathological compare function "compareStrings". It's dependent on the initial state of the list: if you change the initial order to "C","B","A", then there is no exception.
I wouldn't call this a bug in the Sort function - it's simply a requirement that the comparison function is consistent.
using System.Collections.Generic;
class Program
{
static void Main()
{
var letters = new List<string>{"B","C","A"};
letters.Sort(CompareStrings);
}
private static int CompareStrings(string l, string r)
{
if (l == "B")
return -1;
return l.CompareTo(r);
}
}
Are you sure the problem isn't that SomeProp is null?
In particular, with strings or Nullable<T> values.
With strings, it would be better to use:
list.Sort((x, y) => string.Compare(x.SomeProp, y.SomeProp));
(edit)
For a null-safe wrapper, you can use Comparer<T>.Default - for example, to sort a list by a property:
using System;
using System.Collections.Generic;
public static class ListExt {
public static void Sort<TSource, TValue>(
this List<TSource> list,
Func<TSource, TValue> selector) {
if (list == null) throw new ArgumentNullException("list");
if (selector == null) throw new ArgumentNullException("selector");
var comparer = Comparer<TValue>.Default;
list.Sort((x,y) => comparer.Compare(selector(x), selector(y)));
}
}
class SomeType {
public override string ToString() { return SomeProp; }
public string SomeProp { get; set; }
static void Main() {
var list = new List<SomeType> {
new SomeType { SomeProp = "def"},
new SomeType { SomeProp = null},
new SomeType { SomeProp = "abc"},
new SomeType { SomeProp = "ghi"},
};
list.Sort(x => x.SomeProp);
list.ForEach(Console.WriteLine);
}
}
I too have come across this problem (null reference being passed to my custom IComparer implementation) and finally found out that the problem was due to using inconsistent comparison function.
This was my initial IComparer implementation:
public class NumericStringComparer : IComparer<String>
{
public int Compare(string x, string y)
{
float xNumber, yNumber;
if (!float.TryParse(x, out xNumber))
{
return -1;
}
if (!float.TryParse(y, out yNumber))
{
return -1;
}
if (xNumber == yNumber)
{
return 0;
}
else
{
return (xNumber > yNumber) ? 1 : -1;
}
}
}
The mistake in this code was that Compare would return -1 whenever one of the values could not be parsed properly (in my case it was due to wrongly formatted string representations of numeric values so TryParse always failed).
Notice that in case both x and y were formatted incorrectly (and thus TryParse failed on both of them), calling Compare(x, y) and Compare(y, x) would yield the same result: -1. This I think was the main problem. When debugging, Compare() would be passed null string pointer as one of its arguments at some point even though the collection being sorted did not cotain a null string.
As soon as I had fixed the TryParse issue and ensured consistency of my implementation the problem went away and Compare wasn't being passed null pointers anymore.
Marc's answer is useful. I agree with him that the NullReference is due to calling CompareTo on a null property. Without needing an extension class, you can do:
mylist.Sort((x, y) =>
(Comparer<SomePropType>.Default.Compare(x.SomeProp, y.SomeProp)));
where SomePropType is the type of SomeProp
For debugging purposes, you want your method to be null-safe. (or at least, catch the null-ref. exception, and handle it in some hard-coded way). Then, use the debugger to watch what other values get compared, in what order, and which calls succeed or fail.
Then you will find your answer, and you can then remove the null-safety.
Can you run this code ...
mylst.Sort((i, j) =>
{
Debug.Assert(i.SomeProp != null && j.SomeProp != null);
return i.SomeProp.CompareTo(j.SomeProp);
}
);
I stumbled across this issue myself, and found that it was related to a NaN property in my input. Here's a minimal test case that should produce the exception:
public class C {
double v;
public static void Main() {
var test =
new List<C> { new C { v = 0d },
new C { v = Double.NaN },
new C { v = 1d } };
test.Sort((d1, d2) => (int)(d1.v - d2.v));
}
}

How to check for nulls in a deep lambda expression? [duplicate]

This question already has answers here:
Possible pitfalls of using this (extension method based) shorthand
(11 answers)
Closed 9 years ago.
How can I check for nulls in a deep lamda expression?
Say for example I have a class structure that was nested several layers deep, and I wanted to execute the following lambda:
x => x.Two.Three.Four.Foo
I want it to return null if Two, Three, or Four were null, rather than throwing a System.NullReferenceException.
public class Tests
{
// This test will succeed
[Fact]
public void ReturnsValueWhenClass2NotNull()
{
var one = new One();
one.Two = new Two();
one.Two.Three = new Three();
one.Two.Three.Four = new Four();
one.Two.Three.Four.Foo = "blah";
var result = GetValue(one, x => x.Two.Three.Four.Foo);
Assert.Equal("blah", result);
}
// This test will fail
[Fact]
public void ReturnsNullWhenClass2IsNull()
{
var one = new One();
var result = GetValue(one, x => x.Two.Three.Four.Foo);
Assert.Equal(null, result);
}
private TResult GetValue<TModel, TResult>(TModel model, Expression<Func<TModel, TResult>> expression)
{
var func = expression.Compile();
var value = func(model);
return value;
}
public class One
{
public Two Two { get; set; }
}
public class Two
{
public Three Three { get; set; }
}
public class Three
{
public Four Four { get; set; }
}
public class Four
{
public string Foo { get; set; }
public string Bar { get; set; }
}
}
UPDATE:
One solution would be to catch the NullReferenceException like this:
private TResult GetValue<TModel, TResult>(TModel model, Expression<Func<TModel, TResult>> expression)
{
TResult value;
try
{
var func = expression.Compile();
value = func(model);
}
catch (NullReferenceException)
{
value = default(TResult);
}
return value;
}
But I hate to incur the expense of catching an exception that is not, in my mind, exceptional. I expect this to be the case quite often in my domain.
UPDATE 2:
Another solution would be modify the property getters like this:
public class One
{
private Two two;
public Two Two
{
get
{
return two ?? new Two();
}
set
{
two = value;
}
}
}
Which is mostly ok for my domain, but there are times when I really to expect a property to return null. I checked the answer from Josh E as helpful since it comes pretty close to what I need in some cases.
You could do this with a generic helper extension method, something like:
public static class Get {
public static T IfNotNull<T, U>(this U item, Func<U, T> lambda) where U: class {
if (item == null) {
return default(T);
}
return lambda(item);
}
}
var one = new One();
string fooIfNotNull = one.IfNotNull(x => x.Two).IfNotNull(x => x.Three).IfNotNull(x => x.Four).IfNotNull(x => x.Foo);
You can't do that in a concise way. You can either make the lambda multiple lines, or use nested ternary operators:
var result = GetValue(one, x => x.Two == null ? null :
x.Two.Three == null ? null :
x.Two.Three.Four == null ? null :
x.Two.Three.Four.Foo;
Ugly, I know.
Doing this concisely requires an as-yet-unimplemented operator. We considered adding an operator ".?" to C# 4.0 which would have your desired semantics, but unfortunately it did not fit into our budget. We'll consider it for a hypothetical future version of the language.
You can now do using the Maybe project on codeplex.
Syntax is:
string result = One.Maybe(o => o.Two.Three.Four.Foo);
string cityName = Employee.Maybe(e => e.Person.Address.CityName);
I've written an extension method which enables you to do this:
blah.GetValueOrDefault(x => x.Two.Three.Four.Foo);
It uses Expression Trees to build a nested conditional checking for nulls at each node before returning the expression value; the created expression tree is compiled to a Func and cached, so subsequent uses of the same call should run at almost native speed.
You can also pass in a default value to return if you like:
blah.GetValueOrDefault(x => x.Two.Three.Four.Foo, Foo.Empty);
I've written a blog about it here.
I'm not skilled in c#, but maybe there's some way to implement the "andand" pattern from ruby that solves exactly this problem without polluting the implementation.
The concept is also known as the Maybe Monad in Haskell.
The title of this article seems promising.
Always initialize your properties before using them. Add a constructor to class One, Two, Three and Four. In the constructor initialize your properties so they are not null.
You could modify your getters to read something like:
private Two _two;
public Two Two
{
get
{
if (null == _two)
return new Two();
else
return _two;
}
}
I find the coalesce operator useful for this at times. This only helps though if there is a default/null equivalent version of the object you can drop in.
For instance, sometimes when I'm cracking open XML...
IEnumeratable<XElement> sample;
sample.Where(S => (S.Attribute["name"] ?? new XAttribute("name","")).Value.StartsWith("Hello"))...
Depending on how the default objects are retrieved this can be verbose, and the above example is not a great use but you get the idea. For the particular case of reading XML attributes I have an extension method that returns the attribute value or an empty string.
I converted a function that used a lot of if statements to avoid the nulls to the .IFNotNull method for classes that I converted from an XSD that are 4 and 5 levels deep.
Here are a few lines of the converted code:
ProdYear.PRIOR_CUMULATIVE_CARBON_DIOXIDE_VALUE = year.IfNotNull(x => x.PRIOR_CUMULATIVE).IfNotNull(y => y.CARBON_DIOXIDE).IfNotNull(z => z.VALUE).ToDouble();
ProdYear.PRIOR_CUMULATIVE_CARBON_DIOXIDE_UOM = year.IfNotNull(x => x.PRIOR_CUMULATIVE).IfNotNull(y => y.CARBON_DIOXIDE).IfNotNull(z => z.UOM);
Here are some interesting stats about it:
1) This new method took 3.7409 times longer to run that the variation with the If Statements.
2) I decreased my function line count from 157 to 59.
3) CodeRush from DevExpress has a "Maintenance Complexity" score. When I converted to the Lambda statements, it increased from 984 to 2076, which is theoretically much harder to maintain.

Categories