Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Event fields in object initializers #1279

Closed
destrofer opened this issue Sep 6, 2018 · 2 comments
Closed

Event fields in object initializers #1279

destrofer opened this issue Sep 6, 2018 · 2 comments

Comments

@destrofer
Copy link

destrofer commented Sep 6, 2018

ILSpy version: 4.0.0.4319-beta2
Target .NET Runtime: v4.0.30319
Compiled code C# language version: 7.3
Decompiled code C# language version: tried 7.3 and 5.0 with same results

ILSpy sometimes seems to decompile subscription to an event of a newly created object as a field initializer, which is not acceptable by any currently available version of C#.

Original code:

using System;

namespace TestDecompiler
{
    public class Foo {
        public delegate void BarEvent(string baz);

        public string Baz = null;
        
        public event BarEvent OnBar;
        
        public void Bar() {
            OnBar?.Invoke(this.Baz);
        }
    }
    
    class Program {
        public static void Main(string[] args) {
            GetFoo().Bar();
        }
        
        public static Foo GetFoo() {
            var foo = new Foo() {
                Baz = "Baz!"
            };
            foo.OnBar += OnBar;
            return foo;
        }
        
        public static void OnBar(string baz) {
            Console.WriteLine(baz);
        }
    }
}

Decompiled code (2 files combined):

using System;

namespace TestDecompiler
{
    public class Foo
    {
        public delegate void BarEvent(string baz);

        public string Baz;

        public event BarEvent OnBar;

        public void Bar()
        {
            this.OnBar?.Invoke(Baz);
        }
    }

    internal class Program
    {
        public static void Main(string[] args)
        {
            GetFoo().Bar();
        }

        public static Foo GetFoo()
        {
            return new Foo
            {
                Baz = "Baz!",
                OnBar = OnBar // <== This is an error.
            };
        }

        public static void OnBar(string baz)
        {
            Console.WriteLine(baz);
        }
    }
}

However if the new object creation is moved from GetFoo() method to the main():

public static void Main(string[] args) {
    var foo = new Foo() {
        Baz = "Baz!"
    };
    foo.OnBar += OnBar;
    foo.Bar();
}

it decompiles fine (no longer uses field initializers):

public static void Main(string[] args)
{
    Foo foo = new Foo();
    foo.Baz = "Baz!";
    foo.OnBar += OnBar;
    foo.Bar();
}

EDIT: Throwing in some extra cases

Original code:

public static Foo GetFooIf1(int p) {
	Foo foo = null;
	if( p == 1 ) {
		foo = new Foo() {
			Baz = "Baz!"
		};
		foo.OnBar += OnBar;
	}
	return foo;
}

public static Foo GetFooIf2(int p) {
	if( p == 1 ) {
		var foo = new Foo() {
			Baz = "Baz!"
		};
		foo.OnBar += OnBar;
		return foo;
	}
	return null;
}

public static Foo GetFooSwitch(int p) {
	switch( p ) {
		case 1: {
				var foo = new Foo() {
					Baz = "Baz1!"
				};
				foo.OnBar += OnBar;
				return foo;
			}
		case 2: {
				var foo = new Foo() {
					Baz = "Baz2!"
				};
				foo.OnBar += OnBar;
				return foo;
			}
	}
	return null;
}

Decompiled code:

// Perfect
public static Foo GetFooIf1(int p)
{
    Foo foo = null;
    if (p == 1)
    {
        foo = new Foo
        {
            Baz = "Baz!"
        };
        foo.OnBar += OnBar;
    }
    return foo;
}

// Bad
public static Foo GetFooIf2(int p)
{
    if (p == 1)
    {
        return new Foo
        {
            Baz = "Baz!",
            OnBar = OnBar
        };
    }
    return null;
}

// Bad
public static Foo GetFooSwitch(int p)
{
    switch (p)
    {
    case 1:
        return new Foo
        {
            Baz = "Baz1!",
            OnBar = OnBar
        };
    case 2:
        return new Foo
        {
            Baz = "Baz2!",
            OnBar = OnBar
        };
    default:
        return null;
    }
}
@destrofer destrofer changed the title Bug: event fields in object initializers Event fields in object initializers Sep 7, 2018
@Viridovics
Copy link
Contributor

Are you compiling with "Optimize code" option?

@destrofer
Copy link
Author

Yes. It is on by default, so I never think about it. Still even if it is an optimized code maybe ILSpy could check type of field that is in the initializer and move subscription to events right after initialization?

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 10, 2020
ElektroKill pushed a commit to dnSpyEx/ILSpy that referenced this issue Jul 11, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants