Skip to content

Commit

Permalink
Edit README
Browse files Browse the repository at this point in the history
  • Loading branch information
aalmada committed Sep 16, 2023
1 parent 9a31d4d commit ed66676
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 17 deletions.
58 changes: 50 additions & 8 deletions NetFabric.CodeAnalysis/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,62 @@

This package extends the API provided by [Roslyn](https://github.com/dotnet/roslyn/blob/main/docs/wiki/Roslyn-Overview.md). It can be used in the development of [Roslyn Analyzers](https://learn.microsoft.com/en-us/visualstudio/code-quality/roslyn-analyzers-overview) and [Source Generators](https://learn.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/source-generators-overview).

## IsEnumerable() and IsAsyncEnumerable()
## Enumerable type checking

To find if a type is enumerable, it's not enough to check if it implements `IEnumerable`, `IEnumerable<>`, or `IAsyncEnumerable<>`. `foreach` and `await foreach` support several other cases. Use the methods `IsEnumerable` and `IsAsyncEnumerable` instead:
To find if a type is enumerable, it's not enough to check if it implements `IEnumerable`, `IEnumerable<>` or `IAsyncEnumerable<>`. The `foreach` and `await foreach` statements support several other cases.

> NOTE: Check the article ["Efficient Data Processing: Leveraging C#'s foreach Loop"](https://www.linkedin.com/pulse/efficient-data-processing-leveraging-cs-foreach-loop-ant%C3%A3o-almada/) to understand all the possible cases supported by the `foreach` statement.
This package provides extension methods for the interface `ITypeSymbol` that can correctly validate if the type it represents can be used as the source in `foreach` or `await foreach` statements.

### IsEnumerable and IsEnumerator

```csharp
public static bool IsEnumerable(this ITypeSymbol typeSymbol,
Compilation compilation,
[NotNullWhen(true)] out EnumerableSymbols? enumerableSymbols,
out Errors errors);

public static bool IsEnumerator(this ITypeSymbol typeSymbol,
Compilation compilation,
out Errors errors);
```

The methods return `true` if the type represented by `ITypeSymbol` can be used in a `foreach` statement; otherwise `false`. It supports all the cases including when `GetEnumerator()` is defined as an extension method.

`IsEnumerable()` calls `IsEnumerator()` internally to validate the type returned by `GetEnumerator()`. The method `IsEnumerable()` only returns `true` if both the enumerable and the enumerator are valid.

If the method `IsEnumerable()` returns `true`, the `enumerableSymbols` output parameter contains all the `IMethodInfo` and `IPropertySymbol` for the methods and properties that are going to be actually used by the `foreach` statement. The `GetEnumerator()` of the enumerable, the property `Current` and the method `MoveNext()` of the enumerator. It may also contain info for methods `Reset()` and `Dispose()` of the enumerator, if defined.

Is the methods return `false`, the `errors` output parameter indicates why the type is not considered an enumerable. It can be a combination of `Error.MissingGetEnumerator`, `Error.MissingCurrent` and `Error.MissingMoveNext`.

The output parameter also includes a `ForEachUsesIndexer` boolean property that indicates that, although the collection provides an enumerator, `foreach` will use the indexer instead. That's the case for arrays and spans.

You can use these info values to further validate the enumerable and its respective enumerator. For example, use the following to find if the `Current` property of the enumerator returns by reference:

```csharp
using NetFabric.CodeAnalysis;
enumerableSymbols.EnumeratorSymbols.Current.ReturnsByRef;
```

var isEnumerable = typeSymbol.IsEnumerable(compilation, out var enumerableSymbols, out var errors);
### IsAsyncEnumerable and IsAsyncEnumerator

var isAsyncEnumerable = typeSymbol.IsAsyncEnumerable(compilation, out var asyncEnumerableSymbols, out var errors);
```csharp
public static bool IsAsyncEnumerable(this ITypeSymbol typeSymbol,
Compilation compilation,
[NotNullWhen(true)] out AsyncEnumerableSymbols? enumerableSymbols,
out Errors errors);

public static bool IsAsyncEnumerator(this ITypeSymbol typeSymbol,
Compilation compilation,
out Errors errors);
```

The methods return a boolean value indicating if it's an enumerable or enumerator accepted by `foreach`. It supports the cases where `GetEnumerator()` or `GetAsyncEnumerator()` are provided as extension methods.
The methods return `true` if the type represented by `ITypeSymbol` can be used in an `await foreach` statement; otherwise `false`. It supports all the cases including when `GetAsyncEnumerator()` is defined as an extension method.

`IsAsyncEnumerable()` calls `IsAsyncEnumerator()` internally to validate the type returned by `GetAsyncEnumerator()`. The method `IsAsyncEnumerable()` only returns `true` if both the enumerable and the enumerator are valid.

If the method `IsAsyncEnumerable()` returns `true`, the `enumerableSymbols` output parameter contains all the `IMethodInfo` and `IPropertySymbol` for the methods and properties that are going to be actually used by the `await foreach` statement. The `GetAsyncEnumerator()` of the enumerable, the property `Current` and the method `MoveNextAsync()` of the enumerator. It may also contain info for method `DisposeAsync()` of the enumerator, if defined.

If `true`, the first output parameter contains [`IMethodSymbol`](https://docs.microsoft.com/en-us/dotnet/api/microsoft.codeanalysis.imethodsymbol) for the method `GetEnumerator`/`GetAsynEnumerator` of the enumerable, the property `Current` and the method `MoveNext`/`MoveNextAsync` of the enumerator, following the precedences used by Roslyn for the `foreach` and `await foreach` keywords. It may also contain for methods `Reset` and `Dispose`/`DisposeAsync` if defined.
Is the methods return `false`, the `errors` output parameter indicates why the type is not considered an enumerable. It can be a combination of `Error.MissingGetEnumerator`, `Error.MissingCurrent` and `Error.MissingMoveNext`.

If `false`, the second output parameter indicates what error was found. It can be a missing `GetEnumerator()`, missing `Current`, or missing `MoveNext()`.
You can use these info values to further validate the async enumerable or its respective enumerator.
60 changes: 51 additions & 9 deletions NetFabric.Reflection/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,67 @@

This package extends the reflection API.

## IsEnumerable() and IsAsyncEnumerable()
## Enumerable type checking

To find if a type is enumerable, it's not enough to check if it implements `IEnumerable`, `IEnumerable<>`, or `IAsyncEnumerable<>`. `foreach` and `await foreach` support several other cases. Use the methods `IsEnumerable` and `IsAsyncEnumerable` instead:
To find if a type is enumerable, it's not enough to check if it implements `IEnumerable`, `IEnumerable<>` or `IAsyncEnumerable<>`. The `foreach` and `await foreach` statements support several other cases.

> NOTE: Check the article ["Efficient Data Processing: Leveraging C#'s foreach Loop"](https://www.linkedin.com/pulse/efficient-data-processing-leveraging-cs-foreach-loop-ant%C3%A3o-almada/) to understand all the possible cases supported by the `foreach` statement.
This package provides extension methods for the type `Type` that can correctly validate if the type it represents can be used as the source in `foreach` or `await foreach` statements.

### IsEnumerable and IsEnumerator

```csharp
public static bool IsEnumerable(this Type type,
[NotNullWhen(true)] out EnumerableInfo? enumerableInfo,
out Errors errors);

public static bool IsEnumerator(this Type type,
out Errors errors);
```

The methods return `true` if the type represented by `Type` can be used in a `foreach` statement; otherwise `false`.

> NOTE: It does not support the case when `GetEnumerator()` is defined as an extension method. It's not possible to find extension methods using reflection.
`IsEnumerable()` calls `IsEnumerator()` internally to validate the type returned by `GetEnumerator()`. The method `IsEnumerable()` only returns `true` if both the enumerable and the enumerator are valid.

If the method `IsEnumerable()` returns `true`, the `enumerableInfo` output parameter contains all the `MethodInfo` and `PropertySymbol` for the methods and properties that are going to be actually used by the `foreach` statement. The `GetEnumerator()` of the enumerable, the property `Current` and the method `MoveNext()` of the enumerator. It may also contain info for methods `Reset()` and `Dispose()` of the enumerator, if defined.

Is the methods return `false`, the `errors` output parameter indicates why the type is not considered an enumerable. It can be a combination of `Error.MissingGetEnumerator`, `Error.MissingCurrent` and `Error.MissingMoveNext`.

The output parameter also includes a `ForEachUsesIndexer` boolean property that indicates that, although the collection provides an enumerator, `foreach` will use the indexer instead. That's the case for arrays and spans.

You can use these info values to further validate the enumerable and its respective enumerator. For example, use the following to find if the `Current` property of the enumerator returns by reference:

```csharp
using NetFabric.Reflection;
enumerableInfo.EnumeratorSymbols.Current.ReturnsByRef;
```

var isEnumerable = type.IsEnumerable(out var enumerableInfo, out var errors);
### IsAsyncEnumerable and IsAsyncEnumerator

var isAsyncEnumerable = type.IsAsyncEnumerable(out var asyncEnumerableInfo, out var errors);
```csharp
public static bool IsAsyncEnumerable(this Type type,
[NotNullWhen(true)] out AsyncEnumerableInfo? enumerableInfo,
out Errors errors);

public static bool IsAsyncEnumerator(this Type type,
out Errors errors)
```

The methods return a boolean value indicating if it's a valid enumerable or enumerator. It does not support the cases where `GetEnumerator()` or `GetAsyncEnumerator()` are provided as extension methods as it's not possible to find extension methods by using reflection.
The methods return `true` if the type represented by `Type` can be used in an `await foreach` statement; otherwise `false`.

> NOTE: It does not support the case when `GetAsyncEnumerator()` is defined as an extension method. It's not possible to find extension methods using reflection.
`IsAsyncEnumerable()` calls `IsAsyncEnumerator()` internally to validate the type returned by `GetAsyncEnumerator()`. The method `IsAsyncEnumerable()` only returns `true` if both the enumerable and the enumerator are valid.

If the method `IsAsyncEnumerable()` returns `true`, the `enumerableInfo` output parameter contains all the `MethodInfo` and `PropertySymbol` for the methods and properties that are going to be actually used by the `await foreach` statement. The `GetAsyncEnumerator()` of the enumerable, the property `Current` and the method `MoveNextAsync()` of the enumerator. It may also contain info for method `DisposeAsync()` of the enumerator, if defined.

If `true`, the first output parameter contains [`MethodInfo`](https://docs.microsoft.com/en-us/dotnet/api/system.reflection.methodinfo) for the method `GetEnumerator`/`GetAsynEnumerator` of the enumerable, the property `Current` and the method `MoveNext`/`MoveNextAsync` of the enumerator, following the precedences used by Roslyn for the `foreach` and `await foreach` keywords. It may also contain for methods `Reset` and `Dispose`/`DisposeAsync` if defined.
Is the methods return `false`, the `errors` output parameter indicates why the type is not considered an enumerable. It can be a combination of `Error.MissingGetEnumerator`, `Error.MissingCurrent` and `Error.MissingMoveNext`.

If `false`, the second output parameter indicates what error was found. It can be a missing `GetEnumerator()`, missing `Current`, or missing `MoveNext()`.
You can use these info values to further validate the async enumerable or its respective enumerator.

## ExpressionEx
## Expression trees

[NetFabric.Reflection](https://www.nuget.org/packages/NetFabric.Reflection/) contains high level `Expression` generators that makes it easier to handle enumerables in [Expression Trees](https://tyrrrz.me/blog/expression-trees). The code generated is as similar as possible to the one generated by Roslyn for the equivalent keywords.

Expand Down

0 comments on commit ed66676

Please sign in to comment.