7

j'ai une méthode simple qui utilise un bloc itérateur pour revenir un IEnumerable<T>:problème en utilisant des méthodes C# iterator avec la sécurité d'accès au code

IEnumerable<MyItem> GetItems() 
{ 
    foreach (var item in Items) 
    { 
     yield return item; 
    } 
} 

Ordinairement, cette méthode fonctionne très bien, mais si je demande un attribut [SecurityCritical] à la assembly (ou à la classe qui contient la méthode ci-dessus), il lance un TypeLoadException lors de la tentative d'appel de la méthode. Le type qui ne parvient pas à charger est la classe générée par le compilateur qui correspond à la méthode itérateur, et c'est sa méthode GetEnumerator qui est à l'origine du problème, car elle est transparente pour la sécurité.

Pour comparaison, si je modifie la méthode ci-dessus afin qu'il remplit et renvoie un List<MyItem>, tout fonctionne correctement.

Des suggestions?

Merci,

Tim.

+0

Peut-être que '[SecurityCritical]' essaie de vous dire de ne pas utiliser 'var' et d'être plus spécifique au type? – BeemerGuy

+10

@BeemerGuy Je ne sais pas comment cela changerait quoi que ce soit puisque 'var' est du sucre syntaxique et est simplement remplacé par le type approprié par le compilateur. –

+0

Est-ce que c'est pour Silverlight? Ensuite, peu importe, vous n'avez pas la clé secrète pour signer. –

Répondre

3

Il n'est pas la plus élégante chose à faire, donc nous espérons que vous pouvez trouver une meilleure façon, mais vous pouvez toujours renoncer à le code généré par le compilateur et créer votre propre classe qui implémente IEnumerator<MyItem> (et peut-être votre propre classe implémentant IEnumerable<MyItem> - en fonction de la complexité, cela peut rendre les choses plus faciles ou plus difficiles), et ensuite construire l'énumérateur plus ou moins comme vous le feriez dans les jours précédant .NET2.0. Si la logique de votre vrai bloc d'itérateur est très compliquée, vous pourriez trouver le reflet de la classe que le compilateur a créé pour que vous soyez un bon point de départ, bien que parfois le code généré soit plus compliqué (ou du moins, moins lisible) que l'approche qu'on se prendrait.

Il est toujours un peu décevant d'avoir à construire une classe IEnumerator quand yield l'a rendu si agréable pour nous 99% du temps, mais il y a encore des moments où c'est nécessaire, et cela pourrait résoudre votre problème ici.

3

J'ai eu le même problème, dans une application compliquée. Le printemps entre et dit que le type 'blahblah' n'est pas Serializable et qu'il était correct, Voici le code désassemblé du code généré par le compilateur et sûr qu'il n'est pas sérialisable. Peut-être que c'était votre problème aussi, et la solution est ce que vous avez mentionné vous-même parce que la liste est en fait un type sérialisable.

Le code pour générer yield return new KeyValuePair<??? ???>(???,???);

[CompilerGenerated, DebuggerDisplay(@"\{ x = {x}, y = {y} }", Type="<Anonymous Type>")] 
internal sealed class <>f__AnonymousType0<<x>j__TPar, <y>j__TPar> 
{ 
    // Fields 
    [DebuggerBrowsable(DebuggerBrowsableState.Never)] 
    private readonly <x>j__TPar <x>i__Field; 
    [DebuggerBrowsable(DebuggerBrowsableState.Never)] 
    private readonly <y>j__TPar <y>i__Field; 

    // Methods 
    [DebuggerHidden] 
    public <>f__AnonymousType0(<x>j__TPar x, <y>j__TPar y) 
    { 
     this.<x>i__Field = x; 
     this.<y>i__Field = y; 
    } 

    [DebuggerHidden] 
    public override bool Equals(object value) 
    { 
     var type = value as <>f__AnonymousType0<<x>j__TPar, <y>j__TPar>; 
     return (((type != null) && EqualityComparer<<x>j__TPar>.Default.Equals(this.<x>i__Field, type.<x>i__Field)) && EqualityComparer<<y>j__TPar>.Default.Equals(this.<y>i__Field, type.<y>i__Field)); 
    } 

    [DebuggerHidden] 
    public override int GetHashCode() 
    { 
     int num = -576933007; 
     num = (-1521134295 * num) + EqualityComparer<<x>j__TPar>.Default.GetHashCode(this.<x>i__Field); 
     return ((-1521134295 * num) + EqualityComparer<<y>j__TPar>.Default.GetHashCode(this.<y>i__Field)); 
    } 

    [DebuggerHidden] 
    public override string ToString() 
    { 
     StringBuilder builder = new StringBuilder(); 
     builder.Append("{ x = "); 
     builder.Append(this.<x>i__Field); 
     builder.Append(", y = "); 
     builder.Append(this.<y>i__Field); 
     builder.Append(" }"); 
     return builder.ToString(); 
    } 

    // Properties 
    public <x>j__TPar x 
    { 
     get 
     { 
      return this.<x>i__Field; 
     } 
    } 

    public <y>j__TPar y 
    { 
     get 
     { 
      return this.<y>i__Field; 
     } 
    } 
} 
0

Vous pouvez voter pour ce numéro: https://connect.microsoft.com/VisualStudio/feedback/details/667328/yield-and-securitycriticalattribute-problem

[EDIT] Réponse de Microsoft:

Nous avons examiné itérateurs SecurityCritical et décidé ne pas essayer de faire ce travail au moins pour cette version . C'est un effort compliqué et compliqué, et cela ne semble pas trop utile, car l'appel à travers IEnumerator.MoveNext ferait appel à travers une interface non-critique .

Nous y reviendrons probablement dans une version ultérieure; surtout si nous voir des scénarios communs pour cela.