Question

Qu'est-ce que la moyenne de l'instruction?

D'ici

  

ref et sur les paramètres en C # et   ne peut pas être marqué comme variante.

1) Est-ce que cela veut dire que ce qui suit ne peut se faire.

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

2) Ou est-ce que cela signifie que je ne peux pas avoir ce qui suit.

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();
    }

J'ai essayé (2) et cela a fonctionné.

Était-ce utile?

La solution

"" veut dire, grosso modo, "apparaît seulement dans des positions de sortie".

"dans" signifie, grosso modo, "apparaît uniquement dans des positions d'entrée".

La vraie histoire est un peu plus compliqué que cela, mais les mots-clés ont été choisis parce que la plupart du temps c'est le cas.

Considérons une méthode d'une interface ou la méthode représentée par un délégué:

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

Est-ce que T apparaît dans une position d'entrée? Oui. L'appelant peut transmettre une valeur de T à travers l'article; le Foo peut lire callee que. Par conséquent T ne peut pas être marqué "out".

Est-ce que T apparaît dans une position de sortie? Oui. Le callee peut écrire une nouvelle valeur au point, qui peut alors lire l'appelant. Par conséquent T ne peut pas être marqué « dans ».

Par conséquent, si T apparaît dans un « ref » paramètre formel, T ne peut pas être marqué comme dans ou hors.

Let regard sur quelques exemples concrets de la façon dont les choses vont mal. Supposons que cela était légal:

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);

chien bien mes chats, nous avons juste fait une écorce de chat. "Out" ne peut pas être légal.

Qu'en est-il "dans"?

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);

Et nous avons mis juste un chat dans une variable qui ne peut contenir que des chiens. T ne peut pas être marqué « dans » non plus.

Qu'en est-il un paramètre out?

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

? Maintenant, T apparaît seulement dans une position de sortie. Faut-il légal de faire T marqué comme « out »?

Malheureusement pas. « Out » est en fait pas différent de « ref » dans les coulisses. La seule différence entre « out » et « ref » est que le compilateur défend la lecture d'un paramètre avant lui est attribué par l'appelé, et que le compilateur nécessite l'affectation avant le retour du callee normalement. Quelqu'un qui a écrit une implémentation de cette interface dans une langue autre que .NET C # serait en mesure de lire l'article avant qu'il ne soit initialisé, et il pourrait donc être utilisé comme entrée. Nous interdisons donc marquer T comme « out » dans ce cas. C'est regrettable, mais nous ne pouvons rien faire à ce sujet; nous devons respecter les règles de sécurité du type du CLR.

En outre, la règle de « out » paramètres est qu'ils ne peuvent pas être utilisés pour l'entrée avant d'être écrits à . Il n'y a pas de règle qu'ils ne peuvent pas être utilisés pour l'entrée après ils sont écrits. Supposons que nous avons permis

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(); 
    }
}

Une fois de plus, nous avons fait une écorce de chat. Nous ne pouvons pas permettre T d'être "out".

Il est très stupide d'utiliser des paramètres pour l'entrée de cette façon, mais juridique.


MISE À JOUR: C # 7 a ajouté in comme une déclaration de paramètre formel, ce qui signifie que nous avons maintenant deux in et out signifie deux choses; cela va créer une certaine confusion. Permettez-moi de clarifier cela:

  • in, out et ref sur Déclaration de paramètres formels dans une liste de paramètres signifie « ce paramètre est un alias à une variable fournie par l'appelant ».
  • des moyens de ref « le peut callee lire ou écrire la variable aliasées et il faut savoir être attribué avant l'appel.
  • des moyens de out « doivent écrire le callee la variable crénelage via l'alias avant qu'elle ne retourne normalement ». Il a également des moyens que la lecture ne doit pas callee la variable crénelage via l'alias avant de les écrire, car la variable pourrait ne pas être définitivement attribué.
  • des moyens de in « le peut lire la callee variable de crénelage, mais ne pas écrire via l'alias ». Le but de in est de résoudre un problème rare performance, dans lequel un grand struct doit être passé « en valeur », mais il est coûteux de le faire. Comme un détail de mise en œuvre, les paramètres de in sont généralement passés par une valeur moyenne pointeur, qui est plus rapide que la copie par valeur, mais plus lente sur le déréférencement.
  • Du point de vue du CLR, in, out et ref sont la même chose; les règles sur qui lit et écrit les variables à quel moment, ee CLR ne sait pas ou de soins.
  • Comme il est le CLR qui applique les règles sur la variance, les règles applicables à ref appliquent également aux paramètres de in et out.

Par contre, in et out sur les déclarations des paramètres de type signifie « ce paramètre de type ne doit pas être utilisé d'une manière covariante » et « ce paramètre de type ne doit pas être utilisé d'une manière contravariante », respectivement.

Comme indiqué ci-dessus, nous avons choisi in et out ces modificateurs parce que pour si nous voyons IFoo<in T, out U> alors T est utilisé dans des positions « d'entrée » et U est utilisé dans des positions « de sortie ». Bien que ce n'est pas strictement true, il est assez vrai dans le cas d'utilisation de 99,9% qu'il est un mnémotechnique utile.

Il est regrettable que interface IFoo<in T, out U> { void Foo(in T t, out U u); } est illégal parce qu'il semble que cela devrait fonctionner. Il ne peut pas travailler parce que du point de vue du vérificateur CLR, ce sont les deux paramètres de ref et donc en lecture-écriture.

Ceci est juste une de ces situations étranges, inattendues où deux caractéristiques qui devraient logiquement travailler ensemble ne fonctionnent pas bien ensemble pour des raisons de détail de mise en œuvre.

Autres conseils

Cela signifie que vous ne pouvez pas avoir la déclaration suivante:

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

Modifier @Eric Lippert m'a corrigé que celui-ci est encore légal:

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

Il est en fait sens, car le paramètre générique R est pas marqué comme une variante, il ne viole pas la règle. Cependant, celui-ci est toujours illégal:

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

Il est possible d'utiliser covariance avec des paramètres out, mais vous avez besoin de deux structures. Par exemple, vous pouvez mettre le paramètre sur une méthode d'extension:

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; }
}

Mais, le code suivant peut être compilé:

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!
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top