7

I have a simple method that uses an iterator block to return an IEnumerable<T>:

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

Ordinarily, this method works fine, but if I apply a [SecurityCritical] attribute to the assembly (or to the class that contains the above method), it throws a TypeLoadException when attempting to invoke the method. The type that is failing to load is the compiler-generated class that corresponds to the iterator method, and it is its GetEnumerator method that is causing the problem, since it is security transparent.

For comparison, if I modify the above method so that it populates and returns a List<MyItem>, everything works fine.

Any suggestions?

Thanks,

Tim.

Tim Coulter
  • 8,705
  • 11
  • 64
  • 95
  • Maybe the `[SecurityCritical]` is trying to tell you don't use `var` and to be more type-specific? – BeemerGuy Nov 25 '10 at 14:24
  • 10
    @BeemerGuy I don't know how that would change anything since `var` is syntactic sugar and is simply replaced with the appropriate type by the compiler. – Etienne de Martel Nov 25 '10 at 14:25
  • Is this for Silverlight? Then it doesn't matter, you don't have the secret key to sign anyway. – Hans Passant Nov 25 '10 at 15:30
  • @Hans: No, it's for WPF. The assemblies are signed and the code access security is working fine in all other respects. – Tim Coulter Nov 25 '10 at 15:34
  • When you say "invoke" are you saying there's reflection involved? (I can't repro the problem using the simplest possible console app that has the details you've specified) – AakashM Nov 25 '10 at 15:38
  • @AakashM: no, there's no reflection involved. I agree that my setup is probably more complex than I have described, as the method call originates in another AppDomain. I guess I will have to try to eliminate some of the complications in order to uncover the real source of the problem. I was kinda hoping that somebody would have seen this before and have an instant solution. – Tim Coulter Nov 25 '10 at 17:15

3 Answers3

3

It isn't the neatest thing to do, so hopefully you can find a better way, but you could always forgo the compiler-generated code and create your own class that implements IEnumerator<MyItem> (and perhaps your own class implementing IEnumerable<MyItem> - depending on complexity, doing so may make things easier or more difficult), and then build the enumerator more or less as you would in the days before .NET2.0.

If the logic of your real iterator block is very complicated, you might find looking at the reflection of the class the compiler created for you to be a good starting point in doing this, though sometimes the generated code is more complicated (or at least, less readable) than the approach one would take oneself.

It's always a bit disappointing to have to build an IEnumerator class when yield has made it so nice for us 99% of the time, but there are still times when its necessary, and it might solve your problem here.

Jon Hanna
  • 110,372
  • 10
  • 146
  • 251
3

I had the very same problem, in a complicated application. Spring comes in between and said that the 'blahblah' type is not Serializable and sure it was correct, Here is the disassembled code of compiler generated code and sure it's not Serializable. Maybe this was your problem too, and the solution is what you mentioned yourself cause the List is actually a Serializable type.

The code generate for 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;
        }
    }
}
Jahan Zinedine
  • 14,616
  • 5
  • 46
  • 70
0

You can vote for this issue: https://connect.microsoft.com/VisualStudio/feedback/details/667328/yield-and-securitycriticalattribute-problem

[EDIT] Response from Microsoft:

We've looked at SecurityCritical iterators and decided not to try to make that work at least for this release. It is a significant and complicated effort, and it does not seem too useful, as the call through IEnumerator.MoveNext would be calling through a non-critical interface.

We'll probably revisit this again in a later release; especially if we see common scenarios for it.

QrystaL
  • 4,886
  • 2
  • 24
  • 28