En C# Agregar selectmany extiende Linq a un nuevo tipo de mónada, ¿cómo hago lo mismo en vb.net?

StackOverflow https://stackoverflow.com/questions/1467293

  •  13-09-2019
  •  | 
  •  

Pregunta

Un viejo Otra publicación de blog de geek de idiomas Explicar las monads describe agregar un método de extensión SelectMany a C# para extender la sintaxis LINQ a nuevos tipos.

Lo he probado en C# y funciona. Hice una conversión directa a VB.NET y no funciona. ¿Alguien sabe si VB.NET admite esta función o cómo usarla?

Aquí está el código C# que funciona:

class Identity<T> {
    public readonly T Value;
    public Identity(T value) { this.Value = value; }
}
static class MonadExtension {
    public static Identity<T> ToIdentity<T>(this T value) {
        return new Identity<T>(value);
    }
    public static Identity<V> SelectMany<T, U, V>(this Identity<T> id, Func<T, Identity<U>> k, Func<T, U, V> s) {
        return s(id.Value, k(id.Value).Value).ToIdentity();
    }
}
class Program {
    static void Main(string[] args) {
        var r = from x in 5.ToIdentity()
                from y in 6.ToIdentity()
                select x + y;
    }
}

Aquí está el código VB.NET que no funciona (nota: escrito en VS2010, por lo que puede faltar algunas continuaciones de línea):

Imports System.Runtime.CompilerServices

Public Class Identity(Of T)
    Public ReadOnly value As T
    Public Sub New(ByVal value As T)
        Me.value = value
    End Sub
End Class
Module MonadExtensions
    <Extension()> _
    Public Function ToIdentity(Of T)(ByVal value As T) As Identity(Of T)
        Return New Identity(Of T)(value)
    End Function
    <Extension()> _
    Public Function SelectMany(Of T, U, V)(ByVal id As Identity(Of T), ByVal k As Func(Of T, Identity(Of U)), ByVal s As Func(Of T, U, V)) As Identity(Of V)
        Return s(id.value, k(id.value).value).ToIdentity()
    End Function
End Module
Public Module MonadTest
    Public Sub Main()
        ''Error: Expression of type 'Identity(Of Integer)' is not queryable.
        Dim r = From x In 5.ToIdentity() _
                From y In 6.ToIdentity() _
                Select x + y
    End Sub
End Module
¿Fue útil?

Solución

Aparentemente, VB.NET requiere que, además de definir SelectMany, el tipo de destino debe implementar los métodos que desee (por ejemplo, seleccione, dónde, etc.).

Agregue este método a la identidad y el programa compila y funciona:

Public Function [Select](Of R)(ByVal projection As Func(Of T, R)) As Identity(Of R)
    Return projection(value).ToIdentity
End Function

También puede implementarlo como un método de extensión para "LINQ-IFY" tipos existentes:

<Extension()> _
Public Function [Select](Of T, R)(ByVal this As Identity(Of T), ByVal projection As Func(Of T, R)) As Identity(Of R)
    Return projection(this.value).ToIdentity
End Function

Además, VB.NET solo requiere SelectMany si hay múltiples líneas 'de'. Si la expresión es del formulario "de x seleccione x+1", solo debe implementarse el método identity.select.

Otros consejos

El código equivalente también debe ser compatible con VB. Asegúrese de traducir correctamente los métodos de extensión correctamente:

Este C#:

public static class MonadExtensions
{
    public static Identity<T> ToIdentity<T>(this T value)
    {
        return new Identity<T>(value);
    }
}

Se convertiría en este VB:

Imports System.Runtime.CompilerServices

Module MonadExtensions

  <Extension()> _
  Public Function ToIdentity(Of T)(ByVal value As T) As Identity(Of T)
    Return New Identity(Of T)(value)
  End Function

End Module

Actualizar: Su código anterior es correcto, por lo que parece que solo se encuentra con una limitación del compilador VB. Por lo que puedo decir, lo que estás tratando de hacer es legal de acuerdo con las especificaciones del idioma.

Sin embargo, pude engañar al compilador para que aceptara la consulta al tener Identity(Of T) fingir implementar IEnumerable(Of T):

Public Class Identity(Of T)
  Implements IEnumerable(Of T)
  Public ReadOnly value As T
  Public Sub New(ByVal value As T)
    Me.value = value
  End Sub

  Public Function GetEnumerator() As IEnumerator(Of T) _
    Implements IEnumerable(Of T).GetEnumerator

    Throw New InvalidOperationException("This should never be called.")
  End Function
  Public Function GetEnumerator1() As IEnumerator _
    Implements IEnumerable(Of T).GetEnumerator

    Throw New InvalidOperationException("This should never be called.")
  End Function
End Class

Una vez que convencemos al compilador de que es una consulta válida, resuelve correctamente la llamada a su costumbre SelectMany.

Actualización 2: O si, que Estriloso Usted dijo. Lo intenté primero pero aparentemente olvidé el Extension atributo. De la especificación del idioma, algo se considera consultable si, en orden de preferencia ...

  1. Define un método de selección conforme.
  2. Tiene un método asenumerable () o asquerable ().
  3. Tiene un método fundido (de t).
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top