/// <summary> /// Creates an <see cref="AwaitCSharpExpression"/> that represents awaiting an asynchronous operation. /// </summary> /// <param name="operand">An <see cref="Expression" /> that specifies the asynchronous operation to await.</param> /// <param name="info">An <see cref="AwaitInfo"/> that specifies binding information for the await operation.</param> /// <returns>An instance of the <see cref="AwaitCSharpExpression"/>.</returns> public static AwaitCSharpExpression Await(Expression operand, AwaitInfo info) { // NB: This is the overload the C# compiler binds to. RequiresNotNull(operand, nameof(operand)); RequiresNotNull(info, nameof(info)); RequiresCanRead(operand, nameof(operand)); info.RequiresCanBind(operand); return(new AwaitCSharpExpression(operand, info)); }
internal static void CheckUsingResourceType(Type resourceType, AwaitInfo awaitInfo, LambdaExpression patternDispose, bool allowConvertToDisposable = false) { ValidateType(resourceType); var resourceTypeNonNull = resourceType.GetNonNullableType(); Type disposeReturnType; if (patternDispose != null) { var patternDisposeInputType = patternDispose.Parameters[0].Type; if (!AreReferenceAssignable(patternDisposeInputType, resourceTypeNonNull)) { throw Error.UsingPatternDisposeInputNotCompatibleWithResource(patternDisposeInputType, resourceTypeNonNull); } disposeReturnType = patternDispose.ReturnType; } else { Type disposableInterface; if (awaitInfo != null) { disposableInterface = typeof(IAsyncDisposable); disposeReturnType = typeof(ValueTask); } else { disposableInterface = typeof(IDisposable); disposeReturnType = typeof(void); } if (allowConvertToDisposable) { // NB: In the case of foreach, we allow for a conversion to a disposable interface to be emitted. // While this conversion or type check can sometimes be elided at runtime, we call the factory // here for its side-effect of doing the neccessary checks. _ = Expression.Convert(Expression.Variable(resourceType), disposableInterface); } else { // NB: We don't handle implicit conversions here; the C# compiler can emit a Convert node, // just like it does for those type of conversions in various other places. if (!disposableInterface.IsAssignableFrom(resourceTypeNonNull)) { throw LinqError.ExpressionTypeDoesNotMatchAssignment(resourceTypeNonNull, disposableInterface); } } } if (awaitInfo != null) { awaitInfo.RequiresCanBind(Expression.Parameter(disposeReturnType)); } else { if (disposeReturnType != typeof(void)) { throw Error.UsingDisposeShouldReturnVoid(); } } }
internal static ForEachCSharpStatement Make(EnumeratorInfo enumeratorInfo, AwaitInfo awaitInfo, ReadOnlyCollection <ParameterExpression> variables, Expression collection, Expression body, LabelTarget breakLabel, LabelTarget continueLabel, LambdaExpression conversion, LambdaExpression deconstruction) { if (variables.Count == 0) { throw Error.ForEachNeedsOneOrMoreVariables(); } RequiresNotNullItems(variables, nameof(variables)); if (!AreReferenceAssignable(enumeratorInfo.CollectionType, collection.Type)) { throw Error.ForEachCollectionTypeNotCompatibleWithCollectionExpression(enumeratorInfo.CollectionType, collection.Type); } RequiresCanRead(body, nameof(body)); ValidateLoop(body, breakLabel, continueLabel); var firstVariable = variables[0]; var firstVariableType = firstVariable.Type; if (variables.Count == 1) { if (deconstruction != null) { throw Error.ForEachDeconstructionNotSupportedWithOneVariable(); } ValidateConversion(firstVariableType, enumeratorInfo.ElementType, ref conversion); } else { if (deconstruction == null) { throw Error.ForEachDeconstructionRequiredForMultipleVariables(); } ValidateDeconstruction(enumeratorInfo.ElementType, ref conversion, deconstruction, variables); } if (awaitInfo == null) { if (collection.Type == typeof(string) && variables.Count == 1 && firstVariableType == typeof(char) && conversion == null && deconstruction == null) { return(new StringForEachStatement(enumeratorInfo, variables, collection, body, breakLabel, continueLabel)); } else if (collection.Type.IsArray) { if (collection.Type.IsVector()) { if (conversion == null && deconstruction == null) { return(new SimpleArrayForEachCSharpStatement(enumeratorInfo, variables, collection, body, breakLabel, continueLabel)); } else { return(new ArrayForEachCSharpStatement(enumeratorInfo, variables, collection, body, breakLabel, continueLabel, conversion, deconstruction)); } } else { return(new MultiDimensionalArrayForEachCSharpStatement(enumeratorInfo, variables, collection, body, breakLabel, continueLabel, conversion, deconstruction)); } } } else { awaitInfo.RequiresCanBind(enumeratorInfo.MoveNext.Body); } return(new BoundForEachCSharpStatement(enumeratorInfo, variables, collection, body, breakLabel, continueLabel, conversion, deconstruction, awaitInfo)); }