8000 GitHub - atifaziz/Delegating: .NET Standard Library providing delegated implementations of common interfaces
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

.NET Standard Library providing delegated implementations of common interfaces

License

Notifications You must be signed in to change notification settings

atifaziz/Delegating

Repository files navigation

Delegating

Build Status NuGet MyGet

Delegating is a .NET Standard Library that provides delegating implementations of common interfaces, typically those with a single method only. The actual implementation is delegated to an Action or a function (Func<>, Func<,> and so on depending on the arity of the original method).

With version 1.2, Delegating also targets .NET Framework versions 3.5 and up.

Motivation

The .NET Framework has many interfaces with a single method, like IDisposable, that require the same and tedious boilerplate class declaration and code each time. With closures and lambdas, such interfaces can be implemented once and which delegate the actual implementation to, well (surprise, surprise), a delegate! When such interfaces need to be supplied, an implementation can be expressed more succinctly.

Yes, this will be somewhat useless the day C# gains object expressions like F#.

Usage

Try the following example for IDisposable in a C# Interactive (csi) session (assuming Delegating.dll is in the current directory):

#r "Delegating.dll"
using static Delegating.Delegate;
var disposable = Disposable(() => Console.WriteLine("Disposed!"));
disposable.Dispose();
disposable.Dispose(); // Try again!

The session should look like:

Microsoft (R) Visual C# Interactive Compiler version 2.1.0.61520
Copyright (C) Microsoft Corporation. All rights reserved.

Type "#help" for more information.
> #r "Delegating.dll"
> using static Delegating.Delegate;
> var disposable = Disposable(() => Console.WriteLine("Disposed!"));
> disposable.Dispose();
Disposed!
> disposable.Dispose(); // Disposing more than once is ineffective
>

Implementations

Delegated implementations are available for the following interfaces:

The delegated implementation for an IEnumerable<T> becomes particularly interesting when combined with local functions introduced in C# 7. It enables you to write in-line, ad-hoc and anonymous iterators, e.g.:

var singleDigitEvens = Enumerable(() =>
{
    return _(); IEnumerator<int> _()
    {
        for (var x = 0; ; x < 10; x += 2)
            yield return x;
    }
});

Below is a more involved example that does several computations (runnning aggregations) in a single iteration, all as part of a single LINQ comprehension:

var q = 
    from x in Enumerable.Range(1, 20)
    group x by x % 5 == 0 into g
    select new
    {
        g.Key,
        Run =
            from x in Delegating.Delegate.Enumerable(() =>
            {
                return _(); IEnumerator<(int Num, int Min, int Max, int Count, int Sum)> _()
                {
                    var min = int.MaxValue;
                    var max = int.MinValue;
                    var sum = 0;
                    var count = 0;
                    using (var e = g.GetEnumerator())
                    while (e.MoveNext())
                    {
                        var x = e.Current;
                        if (x < min) min = e.Current;
                        if (x > max) max = e.Current;
                        sum += e.Current;
                        yield return (x, min, max, ++count, sum);
                    }
                }
            })
            select new
            {
                x.Num, x.Min, x.Max, x.Count, x.Sum,
                Average = (double) x.Sum / x.Count
            }
    };

The result will be two groups, where the first group will have a key of false (numbers not divisible by 5) and the following sequnce as its run:

Num Min Max Count Sum Average
1 1 1 1 1 1
2 1 2 2 3 1.5
3 1 3 3 6 2
4 1 4 4 10 2.5
6 1 6 5 16 3.2
7 1 7 6 23 3.833333333
8 1 8 7 31 4.428571429
9 1 9 8 40 5
11 1 11 9 51 5.666666667
12 1 12 10 63 6.3
13 1 13 11 76 6.909090909
14 1 14 12 90 7.5
16 1 16 13 106 8.153846154
17 1 17 14 123 8.785714286
18 1 18 15 141 9.4
19 1 19 16 160 10

The second group will have a key of true (numbers divisible by 5) and the following sequnce as its run:

Num Min Max Count Sum Average
5 5 5 1 5 5
10 5 10 2 15 7.5
15 5 15 3 30 10
20 5 20 4 50 12.5

Conflicts

If you statically import Delegating.Delegate members via using static Delegating.Delegate then chances are that you will run into some conflicts with System.Linq, which is a commonly imported namespace in many source files and projects. The conflict specifically occurs between the method Enumerable on Delegating.Delegate and the class Enumerable from System.Linq. The workaround is to simply import System.Linq.Enumerable explicitly. The following C# Interactive session demonstrates the conflict and the workaround:

Microsoft (R) Visual C# Interactive Compiler version 2.2.0.61624
Copyright (C) Microsoft Corporation. All rights reserved.

Type "#help" for more information.
> #r "Delegating.dll"
> using static Delegating.Delegate;
> using System.Linq;
> Enumerable(() => { return _(); IEnumerator<double> _() { yield return Math.PI; } })
DelegatingEnumerable<double> { 3.1415926535897931 }
> // The following won't compile due to a conflict
> Enumerable.Range(1, 10)
(1,1): error CS0119: 'Delegate.Enumerable<T>(Func<IEnumerator<T>>)' is a method, which is not valid in the given context
> // The workaround is to explicitly import Enumerable from System.Linq
> using Enumerable = System.Linq.Enumerable;
> // Now the following compiles just fine
> Enumerable.Range(1, 10)
RangeIterator { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }
> // The Enumerable method from Delegating.Delegate continues to be available:
> Enumerable(() => { return _(); IEnumerator<double> _() { yield return Math.PI; } })
DelegatingEnumerable<double> { 3.1415926535897931 }

For more information and background, see issue #2.

Building

Building the solution requires MSBuild 15. On a Windows host, run build.cmd to build the solution and pack.cmd to build and pack.

On a non-Windows host with .NET Core SDK 2.1.400+ installed, use the dotnet CLI for building and testing:

$ dotnet build -c Debug -f netstandard2.0 src
$ dotnet build -c Release -f netstandard2.0 src
$ dotnet test tests

About

.NET Standard Library providing delegated implementations of common interfaces

Resources

License

Stars

Watchers

Forks

Packages

No packages published
0