C
C#2y ago
rotor_

Implementing IEnumerable<T>

Hi all! I'm trying to simplify a complex return type that I have to repeat in a lot of places Result<IEnumerable<Result<TRow, Faults<TDataFault>>>, Faults<TMetadatFault>> Although I'm pleased with how the type allows me to easily package and aggregate errors that I encounter without having to throw and catch them, the length of the type does no favours to the code's readability. I sought to start to simplify the type a bit by contracting IEnumerable<Result<TRow, Faults<TDataFault>>> down to IRowSeq<TRow, TDataFault> Which I tried to implement using the following code
internal interface IRowSeq<TRow, TDataFault>
: IEnumerable<Result<TRow, Faults<TDataFault>>>
where TRow : IRowType
where TDataFault : Enum
{

public new IEnumerator<Result<TRow, Faults<TDataFault>>> GetEnumerator();

IEnumerator IEnumerable.GetEnumerator() =>
this.GetEnumerator();
}
internal interface IRowSeq<TRow, TDataFault>
: IEnumerable<Result<TRow, Faults<TDataFault>>>
where TRow : IRowType
where TDataFault : Enum
{

public new IEnumerator<Result<TRow, Faults<TDataFault>>> GetEnumerator();

IEnumerator IEnumerable.GetEnumerator() =>
this.GetEnumerator();
}
Although the code compiles, once I started to update the code I use to generate an IEnumerable
private static IRowSeq<TRow, TDataFault> GetRows(ExcelData excelData)
{
ulong rowNumber = 1;

foreach (var row in excelData.Rows.Skip(1)) // By this point in the code the header has been confirmed
{
yield return ParseAndValidateCells(excelData, row, rowNumber);

rowNumber++;
}
}
private static IRowSeq<TRow, TDataFault> GetRows(ExcelData excelData)
{
ulong rowNumber = 1;

foreach (var row in excelData.Rows.Skip(1)) // By this point in the code the header has been confirmed
{
yield return ParseAndValidateCells(excelData, row, rowNumber);

rowNumber++;
}
}
Then I hit csharp(CS1624) - the body of (method) cannot be an iterator block because 'IRowSeq<TRow, TDataFault>' is not an iterator interface type Perhaps I'm looking at this problem from the wrong angle. Could somebody please set me right?
9 Replies
Tvde1
Tvde12y ago
you can only use yield when your method returns an IEnumerable (<T>) or IAsyncEnumerable (<T>) because it creates a class under the hood which implements just IEnumerable and IEnumerator
rotor_
rotor_2y ago
It seems strange to me that it specifically needs to be IEnumerable when an interface implementing IEnumerable should be functionally identical for the purposes of yield
Tvde1
Tvde12y ago
any other interface might contain additional methods that must be implemented, which the generated Enumerable can't
rotor_
rotor_2y ago
Aha yes that makes sense
Tvde1
Tvde12y ago
there could possibly be a check that checks if the interface is ONLY an IEnumerable, then it could pass, but that was never added it'd allow this: https://sharplab.io/#v2:C4LgTgrgdgPgAgBgARwIwBYDcBYAUHvNAZgB4BLKYAPiQBUBTAZ2FQAoBKPAbzyT5VSoUAdiSocuAL4FcASQbNylGguAAmDt1780QuKPF5p+XBWD0wAMwCGAY3pJ5TYCVo0QA0m6RdJQA===
rotor_
rotor_2y ago
In that case do you have any suggestions for how I might start to wrap up this long return type without adding too much boilerplate code responsible for wrapping and unwrapping the data?
Thinker
Thinker2y ago
(IEnumerator<T> as well)
rotor_
rotor_2y ago
CSharp could really use some good type alias features Alright gave this one a go and I think it's a small enough crime that I'll risk the Unsafe cast and make sure it's covered properly with tests. Thanks very much for the solution