Pregunta

¿Qué hace la media afirmación?

Desde aquí

árbitro y fuera parámetros en C # y no pueda ser marcado como variante.

1) ¿Quiere decir que lo siguiente no se puede hacer.

public class SomeClass<R, A>: IVariant<R, A>
{
    public virtual R DoSomething( ref A args )
    {
        return null;
    }
}

2) o significa que no puedo tener lo siguiente.

public delegate R Reader<out R, in A>(A arg, string s);

public static void AssignReadFromPeonMethodToDelegate(ref Reader<object, Peon> pReader)
{
    pReader = ReadFromPeon;
}

static object ReadFromPeon(Peon p, string propertyName)
    {
        return p.GetType().GetField(propertyName).GetValue(p);
    }

static Reader<object, Peon> pReader;

static void Main(string[] args)
    {
        AssignReadFromPeonMethodToDelegate(ref pReader);
        bCanReadWrite = (bool)pReader(peon, "CanReadWrite");

        Console.WriteLine("Press any key to quit...");
        Console.ReadKey();
    }

He intentado (2) y funcionó.

¿Fue útil?

Solución

"fuera" significa, en términos generales, "sólo aparece en las posiciones de salida".

"en" significa, en términos generales, "sólo aparece en posiciones de entrada".

La historia real es un poco más complicado que eso, pero las palabras clave fueron elegidos porque la mayoría de las veces esto es el caso.

Considere un método de una interfaz o el método representado por un delegado:

delegate void Foo</*???*/ T>(ref T item);

¿El T aparece en una posición de entrada? Si. La persona que llama puede pasar un valor de T en medio de elemento; el destinatario de la llamada Foo puede leer eso. Por lo tanto T no puede clasificarse como "fuera".

¿El T aparece en una posición de salida? Si. El destinatario de la llamada puede escribir un nuevo valor al elemento, que a su vez puede leer la persona que llama. Por lo tanto T no puede clasificarse como "en".

Por lo tanto, si T aparece en un "ref" parámetro formal, T no puede ser marcado como dentro o fuera.

Echemos un vistazo a algunos ejemplos reales de cómo las cosas van mal. Supongamos que este fuera legal:

delegate void X<out T>(ref T item);
...
X<Dog> x1 = (ref Dog d)=>{ d.Bark(); }
X<Animal> x2 = x1; // covariant;
Animal a = new Cat();
x2(ref a);

Bien perro mis gatos, que acaba de hacer una corteza gato. "Fuera" no puede ser legal.

¿Qué hay de "en"?

delegate void X<in T>(ref T item);
...
X<Animal> x1 = (ref Animal a)=>{ a = new Cat(); }
X<Dog> x2 = x1; // contravariant;
Dog d = new Dog();
x2(ref d);

Y nosotros sólo hay que poner un gato en una variable que sólo puede contener los perros. T no se puede marcar "en" tampoco.

¿Qué pasa con un parámetro de salida?

delegate void Foo</*???*/T>(out T item);

? Ahora T sólo aparece en una posición de salida. ¿Debería ser legal para tomar T marcado como "fuera"?

Por desgracia no. "Fuera" en realidad no es diferente de "ref" detrás de las escenas. La única diferencia entre "fuera" y "ref" es que el compilador prohíbe la lectura de un parámetro de salida antes de que se le asigna por el destinatario de la llamada, y que el compilador requiere la asignación antes de que el destinatario de la llamada vuelve normalmente. Alguien que escribió una implementación de esta interfaz en un idioma que no sea .NET C # sería capaz de leer el artículo antes de que se ha inicializado, y por lo tanto podría ser utilizado como una entrada. Por lo tanto, prohibimos marca T como "fuera" en este caso. Eso es lamentable, pero no podemos hacer nada al respecto; tenemos que obedecer las reglas de seguridad de tipo CLR.

Por otra parte, el estado de "fuera" parámetros es que no se pueden utilizar para la entrada antes de que se escriben en . No hay una regla que no se pueden utilizar para la entrada después que se escriben. Supongamos que permitió

delegate void X<out T>(out T item);
class C
{
    Animal a;
    void M()
    {
        X<Dog> x1 = (out Dog d) => 
        { 
             d = null; 
             N(); 
             if (d != null) 
               d.Bark(); 
        };
        x<Animal> x2 = x1; // Suppose this were legal covariance.
        x2(out this.a);
    }
    void N() 
    { 
        if (this.a == null) 
            this.a = new Cat(); 
    }
}

Una vez más hemos hecho una corteza de gato. No podemos permitir que T sea "fuera".

Es muy tonto para usar fuera de los parámetros de entrada de esta manera, pero legal.


ACTUALIZACIÓN: C # 7 ha añadido in como una declaración de parámetros formales, lo que significa que ahora tenemos tanto in y out significa dos cosas; esto va a crear cierta confusión. Permítanme aclarar esto:

  • in, out y ref en un declaración de parámetro formal en una lista de parámetros significa "este parámetro es un alias a una variable proporcionada por el llamador".
  • medios ref "el destinatario puede leer ni escribir la variable alias, y tiene que ser conocidos por estar asignado antes de la llamada.
  • medios out "el destinatario de la llamada deben escribir la variable alias mediante el alias antes de que vuelva normalmente". También significa que el destinatario de la llamada no debe leer a través de la variable alias el alias antes de que lo escribe, ya que la variable no puede ser definitivamente asignado.
  • medios in "el destinatario de la llamada puede leer la variable alias pero no escribir en él a través del alias". El propósito de in es resolver un problema de rendimiento rara, en la que una gran estructura debe ser aprobada "por valor", pero es caro para hacerlo. Como un detalle de implementación, los parámetros in se pasan normalmente a través de un valor triple de tamaño, que es más rápido que copiar por valor, pero más lento en el desreferenciar.
  • Desde el punto de vista de CLR, in, out y ref son la misma cosa; las reglas sobre quién lee y escribe qué variables en qué momento, THe CLR no sabe o cuidado.
  • Puesto que es el CLR que hace cumplir las normas sobre la varianza, las reglas que se aplican a ref también se aplican a los parámetros in y out.

En contraste, in y out en declaraciones de parámetros de tipo significa "este parámetro de tipo no debe ser utilizado de una manera covariante" y "este parámetro de tipo no debe ser utilizado de una manera contravariant", respectivamente.

Como se señaló anteriormente, elegimos in y out para esos modificadores porque si vemos IFoo<in T, out U> entonces T se utiliza en posiciones "entrada" y U se utiliza en posiciones "salida". A pesar de que no es estrictamente Es cierto, es bastante cierto en el caso de uso del 99,9% que es una regla mnemotécnica útil.

Es desafortunado que interface IFoo<in T, out U> { void Foo(in T t, out U u); } es ilegal, ya que parece que se debe trabajar. No puede trabajar porque desde la perspectiva del verificador CLR, esos son los dos parámetros ref y, por tanto, de lectura y escritura.

Esta es sólo una de esas situaciones no deseadas extraños, donde dos características que lógicamente deben trabajar juntos no funcionan bien juntos por razones detalle de implementación.

Otros consejos

Esto significa que no puede tener la siguiente declaración:

public delegate R MyDelegate<out R, in A>(ref A arg);

Editar @Eric Lippert me corrigió que éste sigue siendo legal:

public delegate void MyDelegate<R, in A>(A arg, out R s);

En realidad, tiene sentido, ya que el parámetro genérico R no está marcado como una variante, por lo que no viola la regla. Sin embargo, éste sigue siendo ilegal:

public delegate void MyDelegate<out R, in A>(A arg, out R s);

Es posible utilizar covarianza con parámetros out, pero necesita dos estructuras. Por ejemplo, usted puede poner el parámetro a cabo en un método de extensión:

public static class ResultExtension{
    public static bool TryGetValue<T>(this IResult<T> self, out T res) {
        if (self.HasValue) {
            res = self.Value;
            return true;

        }
        res = default;
        return false;
    }
}

public interface IResult<out T>
{
    bool HasValue { get; }
    T Value { get; }
}

Sin embargo, el siguiente código puede ser compilado:

interface IFoo<out T>
{
    T Get();

    //bool TryGet(out T value); // doesn't work: Invalid variance: The type parameter 'T' must be invariantly valid on 'IFoo<T>.TryGet(out T)'. 'T' is covariant.

    bool TryGet(Action<T> value); // works!
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top