Custom Nullable<T> Extension Methods and SelectMany

There are extension methods for Nullable<T> like below.

using System;
using System.Runtime.CompilerServices;

namespace DoNotationish
{
    public static class NullableExtensions
    {
        public static U? Select<T, U>(this T? nullableValue, Func<T, U> f)
            where T : struct
            where U : struct
        {
            if (!nullableValue.HasValue) return null;
            return f(nullableValue.Value);
        }

        public static V? SelectMany<T, U, V>(this T? nullableValue, Func<T, U?> bind, Func<T, U, V> f)
            where T : struct
            where U : struct
            where V : struct
        {
            if (!nullableValue.HasValue) return null;
            T value = nullableValue.Value;
            U? bindValue = bind(value);
            if (!bindValue.HasValue) return null;
            return f(value, bindValue.Value);
        }
    }
}

This allows Nullable<T> to be used in query syntax. The following tests will pass.

        [Test]
        public void Test1()
        {
            int? nv1 = 5;
            int? nv2 = 3;
            var q = from v1 in nv1
                    from v2 in nv2
                    select v1 + v2;
            Assert.AreEqual(8, q);
        }

        [Test]
        public void Test2()
        {
            int? nv1 = null;
            int? nv2 = 3;
            var q = from v1 in nv1
                    from v2 in nv2
                    select v1 + v2;
            Assert.IsNull(q);
        }

However, if you try to chain 3 or more, it will be treated as an anonymous type and will not compile.

        [Test]
        public void Test3()
        {
            int? nv1 = 5;
            int? nv2 = 3;
            int? nv3 = 8;
            var q = from v1 in nv1
                    from v2 in nv2  // Error CS0453: anonymous type is not struct
                    from v3 in nv3
                    select v1 + v2 + v3;
            Assert.AreEqual(16, q);
        }

You can work around this issue by manually specifying to use ValueTuple as below, but this is ugly.

        [Test]
        public void Test3_()
        {
            int? nv1 = 5;
            int? nv2 = 3;
            int? nv3 = 8;
            var q = from v1 in nv1
                    from v2 in nv2
                    select (v1, v2) into temp      // ugly
                    from v3 in nv3
                    select temp.v1 + temp.v2 + v3; // ugly
            Assert.AreEqual(16, q);
        }

These simplified examples can be solved simply by using the + operator: var q = nv1 + nv2 + nv3;

However, you would find it more convenient to work with user-defined structs if you could write it fluently. Is there any good way?



Read more here: https://stackoverflow.com/questions/65745802/custom-nullablet-extension-methods-and-selectmany

Content Attribution

This content was originally published by topica at Recent Questions - Stack Overflow, and is syndicated here via their RSS feed. You can read the original post over there.

%d bloggers like this: