Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
248 views
in Technique[技术] by (71.8m points)

extension methods - Extending the C# Coalesce Operator

Before I explain what I want to do, if you look at the following code, would you understand what it's supposed to do? (updated - see below)

Console.WriteLine(
  Coalesce.UntilNull(getSomeFoo(), f => f.Value) ?? "default value");

C# already has a null-coalescing operator that works quite well on simple objects but doesn't help if you need to access a member of that object.

E.g.

Console.WriteLine(getSomeString()??"default");

works very well but it won't help you here:

public class Foo
{
  public Foo(string value) { Value=value; }
  public string Value { get; private set; }
}

// this will obviously fail if null was returned
Console.WriteLine(getSomeFoo().Value??"default"); 

// this was the intention
Foo foo=getSomeFoo();
Console.WriteLine(foo!=null?foo.Value:"default");

Since this is something that I come across quite often I thought about using an extension method (old version):

public static class Extension
{
  public static TResult Coalesce<T, TResult>(this T obj, Func<T, TResult> func, TResult defaultValue)
  {
    if (obj!=null) return func(obj);
    else return defaultValue;
  }

  public static TResult Coalesce<T, TResult>(this T obj, Func<T, TResult> func, Func<TResult> defaultFunc)
  {
    if (obj!=null) return func(obj);
    else return defaultFunc();
  }
}

Which allows me to write:

Console.WriteLine(getSomeFoo().Coalesce(f => f.Value, "default value"));

So would you consider this code to be readable? Is Coalesce a good name?

Edit 1: removed the brackets as suggested by Marc

Update

I really liked lassevk's suggestions and Groo's feedback. So I added overloads and didn't implement it as an extension method. I also decided that defaultValue was redundant because you could just use the existing ?? operator for that.

This is the revised class:

public static class Coalesce
{
  public static TResult UntilNull<T, TResult>(T obj, Func<T, TResult> func) where TResult : class
  {
    if (obj!=null) return func(obj);
    else return null;
  }

  public static TResult UntilNull<T1, T2, TResult>(T1 obj, Func<T1, T2> func1, Func<T2, TResult> func2) where TResult : class
  {
    if (obj!=null) return UntilNull(func1(obj), func2);
    else return null;
  }

  public static TResult UntilNull<T1, T2, T3, TResult>(T1 obj, Func<T1, T2> func1, Func<T2, T3> func2, Func<T3, TResult> func3) where TResult : class
  {
    if (obj!=null) return UntilNull(func1(obj), func2, func3);
    else return null;
  }

  public static TResult UntilNull<T1, T2, T3, T4, TResult>(T1 obj, Func<T1, T2> func1, Func<T2, T3> func2, Func<T3, T4> func3, Func<T4, TResult> func4) where TResult : class
  {
    if (obj!=null) return UntilNull(func1(obj), func2, func3, func4);
    else return null;
  }
}

Sample usage:

Console.WriteLine(
  Coalesce.UntilNull(getSomeFoo(), f => f.Value) ?? "default value");

Another sample:

public class Bar
{
  public Bar Child { get; set; }
  public Foo Foo { get; set; }
}

Bar bar=new Bar { Child=new Bar { Foo=new Foo("value") } };

// prints "value":
Console.WriteLine(
  Coalesce.UntilNull(bar, b => b.Child, b => b.Foo, f => f.Value) ?? "null");

// prints "null":
Console.WriteLine(
  Coalesce.UntilNull(bar, b => b.Foo, f => f.Value) ?? "null");
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Yes, I would understand it. Yes, coalesce is a good name. Yes, it would be better if C# had a null-safe dereferencing operator like Groovy and some other languages :)

Update

C# 6 has such an operator - the null conditional operator, ?. For example:

var street = customer?.PrimaryAddress?.Street;

Use it in conjunction with the original null-coalescing operator if you still want a default. For example:

var street = customer?.PrimaryAddress?.Street ?? "(no address given)";

Or, based on the original code in the question:

Console.WriteLine(getSomeFoo()?.Value ?? "default"); 

Noting, of course, that providing a default this way works only if it's okay to use that default value even when the final property value is available but set to null for some reason.

The result of an expression x?.y is null if x evaluates to null; otherwise it's the result of x.y. Oh, and you can use it for conditional method invocation, too:

possiblyNull?.SomeMethod();

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...