Alternative to using Func to return a Span<T> or other ref struct [duplicate]
Asked Answered
P

1

5

How can one pass as a parameter a method that returns a Span?

using System.Memory;   
public Span<byte> CallSpanFactory1(Func<Span<byte>> spanFactory)
{
    return spanFactory();
}

This code returns the error "the type 'Span' may not be used as a type argument" (which makes sense, since using a ref struct as a generic type argument could lead to boxing and is thus prohibited).

Politesse answered 5/3, 2018 at 21:57 Comment(11)
As pointed out by @Servy, the answer is similar to the answer to the question #2463314 but that question is not directly on point. First, a Span<byte> can be passed as a parameter without the ref keyword, and the delegate can omit the ref keyword. Second, the error resulting is different from the error Third, that question does not deal with returning a ref struct or other ref value. With Func<Span<T>>, the span is being returned by the function.Politesse
You are correct that some people might not realize that the questions are fundamentally the same question when seeking out an answer for this question, as they might not realize that they're actually asking about a delegate that passes a value by reference. That's precisely what duplicates are for. If people find your question instead of the canonical, they can then be directed to the canonical answer. That an alternate phrasing of a question makes it harder to find doesn't make the quesitons not duplicates, it just makes them useful duplicates.Aegyptus
Good point @Servy, but this is not about "a delegate that passes a value by reference". A ref struct is NOT always passed by reference. If RS is a ref struct type, then method A(ref RS x) is different from A(RS x). Moreover, this is also a question about a delegate that returns a ref struct value. That's also a different beast. The technique to use to resolve the problem is the same, but the question is different.Politesse
How is the part of the function signature uses the ref type a relevant difference in the question? A difference in the question that is not relevant to the answer is still a duplicate question. In this case it doesn't matter what part of the signature uses a ref type, only that one is used somewhere in the signature.Aegyptus
The pages are superficially similar in that the three-word version of the answer is "use a delegate," but the challenge is different (returning a ref struct vs. passing an object by reference), the reason for the problem is different (cannot have a ref struct as a generic parameter even when it is being used by value because it cannot be boxed vs. cannot have a ref generic parameter), and the answer is accordingly different (no "ref", variable is returned by the delegate). So, you're detouring a user from the page with the correct answer to a page where the answer can be gleaned by analogy.Politesse
How are the differences relevant to the answer. Differences in the question that aren't relevant to determining the answer are still duplicates. There is no differences in the answers here, so whatever differences exist in the question demonstrably aren't relevant to the answer.Aegyptus
public delegate object MethodNameDelegate(ref float y); is not the same as public delegate Span<byte> MethodNameDelegate();. That a delegate works when you want to pass a parameter by reference does not imply that a delegate will work when you want to return a ref struct. The designers of C# 7.2 made specific decisions about when and how ref structs could be returned (goo.gl/pJcNHi), so it is not obvious that the code on the other page addresses the same problem or that changes to that code will work. Thus, the more precise answer is more helpful to the user.Politesse
If you feel that the information in that answer isn't sufficient to answer this question then why did you post an answer that didn't contain any information not found in that answer. By posting that answer you've demonstrated that you feel it's a sufficient answer to the question. If there was additional information that you felt needed to be in an answer to this question then why didn't you include it in your answer?Aegyptus
@Aegyptus At this point, we're probably talking past each other and should stop. The answer posted below includes code different from the code on the other page. The delegate returns a Span<byte>. It does not have a ref parameter. Your statement that the answer below "didn't contain any information not found in that answer" is false. The syntax is different! But I understand and respect your view that it would be better to let programmers figure out the specific answer below based on the analogous code with slightly different syntax on the other page.Politesse
Sure, and the name of the delegate is different, and the name of the instance of the delegate is different, and you didn't show an example method that is assigned to the delegate, but the point is that none of those differences are relevant. All that matters is that you cannot use a reference type in the delegate's signature. That is the answer, and none of the infinite number of different variations of that makes the questions different quesitons requiring that same answer to be repeated.Aegyptus
Let us continue this discussion in chat.Politesse
P
6

This is a case in which one must use a delegate and cannot use Func or Action.

using System.Memory;
public delegate Span<byte> SpanFactoryFunc();

public Span<byte> CallSpanFactory(SpanFactoryFunc spanFactory)
{
    return spanFactory();
}

There are other cases as well in which one must use a delegate because of limitations in generic type parameters. This question identifies one such situation -- where one wishes to pass as a parameter a method that includes a parameter being passed by reference. That cannot be accomplished with Func or Action, because there is no such thing as a ref generic type parameter. There is no syntax like Func<ref float, object>.

The limitation on generic type parameters here is different: There is no syntax like Func<MyRefStruct> or Func<MyRefStruct, object> even without the "ref" modifier. This is NOT because MyRefStruct will always be implicitly passed by reference. Ref structs are not always implicitly passed by reference. They can be passed either by reference or value. Similarly, ref structs are not always returned as "ref returns".

It follows that the form of the delegate matters -- the following two delegates will have different effects:

public ref struct MyRefStruct
{
    // ...
}

public delegate void MyRefStructMutator(ref MyRefStruct s); // s is passed by reference
public delegate void MyRefStructAction(MyRefStruct s); // s is passed by value

Note that if instead of ref struct MyRefStruct, we had a struct or a class called MyType, there would have been no need for a delegate analogous to MyRefStructAction; one could simply use Action<MyType>. Similarly, one could use Func<MyType> to return MyType. It is only with the advent of ref structs that using a delegate becomes necessary when passing a method that passes a parameter by value or returns by value.

Politesse answered 5/3, 2018 at 21:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.