`IEnumerable<T, TEnumerator>` for more precise type propagation in C♯ generics.
/* See https://geelaw.blog/entries/csharp-generics-duck-typing/ for the context. | |
Copyright (c) 2020 by Gee Law | |
Permission is hereby granted, free of charge, to any person obtaining | |
a copy of this software and associated documentation files (the “Software”), | |
to deal in the Software without restriction, including without limitation | |
the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
and/or sell copies of the Software, and to permit persons to whom | |
the Software is furnished to do so, subject to the following conditions: | |
The above copyright notice and this permission notice shall be included | |
in all copies or substantial portions of the Software. | |
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
DEALINGS IN THE SOFTWARE. | |
*/ | |
using System; | |
using System.Collections; | |
using System.Collections.Generic; | |
/* Expected output: | |
------------------------------------------------------------ | |
UseIEnumerable | |
IEnumerable.GetEnumerator() | |
IEnumerator.MoveNext | |
IEnumerator.Current | |
------------------------------------------------------------ | |
UseIEnumerableT | |
IEnumerable<int>.GetEnumerator() | |
IEnumerator.MoveNext | |
IEnumerator<int>.Current | |
------------------------------------------------------------ | |
UseIEnumerableTTEnumerator | |
IEnumerable<int, MyEnumerator>.GetEnumerator() | |
IEnumerator.MoveNext | |
IEnumerator<int>.Current | |
------------------------------------------------------------ | |
UsePreciseType | |
MyEnumerable.GetEnumerator() | |
MyEnumerator.MoveNext | |
MyEnumerator.Current | |
*/ | |
namespace Example | |
{ | |
class Program | |
{ | |
interface IEnumerable<out T, out TEnumerator> : IEnumerable<T> | |
where TEnumerator : IEnumerator<T> | |
{ | |
new TEnumerator GetEnumerator(); | |
} | |
// Enumerates 1 to 9. | |
class MyEnumerator : IEnumerator<int> | |
{ | |
private int count, current; | |
public MyEnumerator() { Reset(); } | |
// IDisposable is not our focus. | |
public void Dispose() { } | |
// This method is not used in expanded foreach. | |
// Generally, it should not be used. | |
public void Reset() { count = 0; } | |
public int Current | |
{ | |
get | |
{ | |
if (current == 1) | |
{ | |
Console.WriteLine("MyEnumerator.Current"); | |
} | |
return count; | |
} | |
} | |
int IEnumerator<int>.Current | |
{ | |
get | |
{ | |
if (current == 1) | |
{ | |
Console.WriteLine("IEnumerator<int>.Current"); | |
} | |
return current; | |
} | |
} | |
object IEnumerator.Current | |
{ | |
get | |
{ | |
if (current == 1) | |
{ | |
Console.WriteLine("IEnumerator.Current"); | |
} | |
return current; | |
} | |
} | |
private bool MoveNextImpl() | |
{ | |
if (count == 10) { return false; } | |
current = ++count; | |
return true; | |
} | |
public bool MoveNext() | |
{ | |
if (count == 0) | |
{ | |
Console.WriteLine("MyEnumerator.MoveNext"); | |
} | |
return MoveNextImpl(); | |
} | |
// IEnumerator<T> does not have a separate MoveNext method. | |
bool IEnumerator.MoveNext() | |
{ | |
if (count == 0) | |
{ | |
Console.WriteLine("IEnumerator.MoveNext"); | |
} | |
return MoveNextImpl(); | |
} | |
} | |
class MyEnumerable : IEnumerable<int, MyEnumerator> | |
{ | |
public MyEnumerable() { } | |
public MyEnumerator GetEnumerator() | |
{ | |
Console.WriteLine("MyEnumerable.GetEnumerator()"); | |
return new MyEnumerator(); | |
} | |
MyEnumerator IEnumerable<int, MyEnumerator>.GetEnumerator() | |
{ | |
Console.WriteLine("IEnumerable<int, MyEnumerator>.GetEnumerator()"); | |
return new MyEnumerator(); | |
} | |
IEnumerator<int> IEnumerable<int>.GetEnumerator() | |
{ | |
Console.WriteLine("IEnumerable<int>.GetEnumerator()"); | |
return new MyEnumerator(); | |
} | |
IEnumerator IEnumerable.GetEnumerator() | |
{ | |
Console.WriteLine("IEnumerable.GetEnumerator()"); | |
return new MyEnumerator(); | |
} | |
} | |
static void UseIEnumerable(IEnumerable enumerable) | |
{ | |
Console.WriteLine(new string('-', 60)); | |
Console.WriteLine("UseIEnumerable"); | |
foreach (var item in enumerable) { } | |
} | |
static void UseIEnumerableT<T>(IEnumerable<T> enumerable) | |
{ | |
Console.WriteLine(new string('-', 60)); | |
Console.WriteLine("UseIEnumerableT"); | |
foreach (var item in enumerable) { } | |
} | |
// Less useful. | |
static void UseTEnumerableT<TEnumerable, T>(TEnumerable enumerable) | |
where TEnumerable : IEnumerable<T> | |
{ | |
Console.WriteLine(new string('-', 60)); | |
Console.WriteLine("UseTEnumerableT"); | |
foreach (var item in enumerable) { } | |
} | |
static void UseIEnumerableTTEnumerator<T, TEnumerator> | |
(IEnumerable<T, TEnumerator> enumerable) | |
where TEnumerator : IEnumerator<T> | |
{ | |
Console.WriteLine(new string('-', 60)); | |
Console.WriteLine("UseIEnumerableTTEnumerator"); | |
foreach (var item in enumerable) { } | |
} | |
// Less useful. | |
static void UseTEnumerableTTEnumerator<TEnumerable, T, TEnumerator> | |
(TEnumerable enumerable) | |
where TEnumerable : IEnumerable<T, TEnumerator> | |
where TEnumerator : IEnumerator<T> | |
{ | |
Console.WriteLine(new string('-', 60)); | |
Console.WriteLine("UseTEnumerableTTEnumerator"); | |
foreach (var item in enumerable) { } | |
} | |
static void UsePreciseType(MyEnumerable enumerable) | |
{ | |
Console.WriteLine(new string('-', 60)); | |
Console.WriteLine("UsePreciseType"); | |
foreach (var item in enumerable) { } | |
} | |
static void Main() | |
{ | |
var enumerable = new MyEnumerable(); | |
UseIEnumerable(enumerable); | |
UseIEnumerableT(enumerable); | |
// UseTEnumerableT(enumerable); | |
UseIEnumerableTTEnumerator(enumerable); | |
// UseTEnumerableTTEnumerator(enumerable); | |
UsePreciseType(enumerable); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment