Tag Archives: clr

The inline if statement in C# : Surprise !

NET%20LogoHi, I got a strange issue today I posted on stackoverflow.

The Issue

Here’s the situation, I’ve abstracted the name and logic to focus on the issue. Got 3 types, A, B and C. B & C have implicit operators defined to convert to A object.

public class A
{
    public static implicit operator A(B input){ /* Convert B to A */ }
    public static implicit operator A(C input) { /* Convert C to A*/ }
}

public class B { }
public class C { }

Then, when I do this, the code compile and work fine :

A myObject = null;
if (condition)
    myObject = new B();
else
    myObject = new C();

But when I write the same logic with an inline if, I got an error :

A myObject = condition ? new B() : new C();
Type of conditional expression cannot be determined because there is no implicit conversion between 'B' and 'C'

WHY ? Why does it work in classical if statement and not in inline if statement ?

The Answer

1775769

Here’s his answer :

Absolutely. The type of a conditional operator expression must either be the type of the second operand or the type of the third operand. (And if those two types aren’t the same, exactly one of the types has to be implicitly convertible to the other.) The compiler doesn’t try to find a “lower common denominator” type, and the use of the result isn’t important either (the compiler doesn’t “notice” that you’re assigning the result to a variable of type A).

You can fix this yourself by just explicitly casting either operand:

A myObject = condition ? (A) new B() : new C();

or

A myObject = condition ? new B() : (A) new C();

Note that this isn’t limited to user-defined conversion operators; the same is true for simple reference conversions based on derived classes:

Button x = new Button();
String y = "foo";
object z = condition ? x : y; // Compile-time error

See section 7.14 of the C# specification for more details.

Behind the scene

But the answer wasn’t totally complete for me. So I took my lovely decompiler and anaylyze what’s generated in the case of an inline and classical if. The code is “self explanatory” and I’ve commented each MSIL instruction to make it readable.

static void ClassicalIf(bool condition)
{
    int i = 0;
    if (condition)
        i = 1;
    else
        i = 2;
}

static void InlineIf(bool condition)
{
    int i = condition ? 1 : 2;
}

For the inline if :

.method private hidebysig static void InlineIf(bool condition) cil managed
{
    .maxstack 1
    .locals init (
        [0] int32 i)
    L_0000: nop 
    L_0001: ldarg.0         -- Load argument '0' onto the stack
    L_0002: brtrue.s L_0007 -- Branch to L_0007 if value is non-zero
    L_0004: ldc.i4.2        -- Push 2 onto the stack
    L_0005: br.s L_0008     -- Branch to L_0008
    L_0007: ldc.i4.1        -- Push 1 onto the stack
    L_0008: nop 
    L_0009: stloc.0         -- Pop from stack into local variable 0
    L_000a: ret 
}

And here’s the one for the “normal” if :

.method private hidebysig static void ClassicalIf(bool condition) cil managed
{
    .maxstack 2
    .locals init (
        [0] int32 i,
        [1] bool CS$4$0000) -- Additional bool for if
    L_0000: nop 
    L_0001: ldc.i4.0        -- Push 0 onto the stack
    L_0002: stloc.0         -- Pop from stack into local variable '0'
    L_0003: ldarg.0         -- Load argument '0' onto the stack
    L_0004: ldc.i4.0        -- Push 0 onto the stack 
    L_0005: ceq             -- Push 1 if value1 equals value2 (on stack), else push 0.
    L_0007: stloc.1         -- Pop from stack into local variable '1'
    L_0008: ldloc.1         -- Load local variable '1' onto stack.
    L_0009: brtrue.s L_000f -- Branch to L_000f if value is non-zero
    L_000b: ldc.i4.1        -- Push 1 onto the stack 
    L_000c: stloc.0         -- Pop from stack into local variable '0'
    L_000d: br.s L_0011     -- Branch to L_0011
    L_000f: ldc.i4.2        -- Push 2 onto the stack 
    L_0010: stloc.0         -- Pop from stack into local variable '0'
    L_0011: ret 
}

Now same thing with an additional cast :

static void ClassicalIf(bool condition)
{
    object i = 0;
    if (condition)
        i = new object();
    else
        i = 2;
}
    
static void InlineIf(bool condition)
{
    object i = condition ? new object() : 2;
}

For the inline if :

.method private hidebysig static void InlineIf(bool condition) cil managed
{
    .maxstack 1
    .locals init (
        [0] object i)
    L_0000: nop 				
    L_0001: ldarg.0 			-- Load argument '0' onto the stack
    L_0002: brtrue.s L_000c     -- Branch to L_000c if value is non-zero (true)
    L_0004: ldc.i4.2            -- Push 2 onto the stack
    L_0005: box int32           -- Boxing
    L_000a: br.s L_0011         -- Branch to L_0008
    L_000c: newobj instance void-- Create new object  
    L_0011: nop                                                     
    L_0012: stloc.0 			-- Pop from stack into local variable 0
    L_0013: ret 
}

And here’s the one for the “normal” if :

.method private hidebysig static void ClassicalIf(bool condition) cil managed
{
    .maxstack 2
    .locals init (
        [0] object i,
        [1] bool CS$4$0000)
    L_0000: nop 					-- Push 0 onto the stack
    L_0001: ldc.i4.0                -- Pop from stack into local variable '0'
    L_0002: box int32               -- Boxing
    L_0007: stloc.0                 -- Pop from stack into local variable 0
    L_0008: ldarg.0                 -- Load argument '0' onto the stack
    L_0009: ldc.i4.0                -- Push 0 onto the stack 
    L_000a: ceq                     -- Push 1 if value1 equals value2 (on stack), else push 0.
    L_000c: stloc.1                 -- Pop from stack into local variable '1'
    L_000d: ldloc.1                 -- Load local variable '1' onto stack.
    L_000e: brtrue.s L_0018         -- Branch to L_0018 if value is non-zero (true)
    L_0010: newobj instance void [ms-- Create new object
    L_0015: stloc.0                 -- Pop from stack into local variable '0'
    L_0016: br.s L_001f             -- Branch to L_001f
    L_0018: ldc.i4.2                -- Push 2 onto the stack 
    L_0019: box int32               -- Boxing
    L_001e: stloc.0 				-- Pop from stack into local variable '0'
    L_001f: ret 
}

Conclusion

1) So I will also try to use inline if as possible. Less instructions (so CPU) and less memory usage.
2) Also it’s now crystal clear when we see how an inline if is converted to MSIL 🙂

Catch you next time and keep it bug free !

DotNet : Embarquer les DLL dans son application

Salut à tous, cette nuit, pour les besoins d’un programme, j’ai voulu embarquer mes DLL dans mon application. Les puristes me diront : « Quoi ? tu veux utiliser une DLL comme une librarie statique ??? » Et bien… oui et non… Les 2 DLL utilisées ne le sont que pour le programme en question. Je voulais donner la possibilité d’utiliser le programme comme un single file executable et non une archive à décompresser etc… Le problème a été le WPF. En effet, si des outils comme ILMerge fonctionnent très bien sur des applis consoles ou winforms, les assembly de WPF n’ont pas l’air d’apprécier ce merging complet. Voici donc un solution que j’ai trouvé et que je vous partage.

Alors la solution n’est pas de moi. Mais je l’ai trouvé tellement mal expliqué, pour quelqu’un qui ne fait pas/plus ( 😉 ) joujou avec la CLR et l’interop, que je me sens obligé de détailler un peu le bidule pour le commun des dev’s.

Dans notre exemple nous aurons une application console dans laquelle on appelle la DLL XYZ. L’assembly de notre projet est MyLoader et la DLL se trouve dans un dossier nommé DLL.
Voici a quoi cela ressemble au début :

J’ai donc une référence vers ma DLL tout ce qui a de plus classique (référence de DLL, pas du projet de la DLL !)
Le problème est que en output de ma compilation j’obtiens deux fichier, MyLoader.exe et XYZ.DLL.
Pour régler cela nous allons d’abord rajouter un dossier DLL au projet et y mettre notre DLL comme une ressource incorporée. Pour cela il faut se rendre sur la propriété du fichier et sélectionner… Ressource incorporé ^^

Ensuite nous allons passer l’attribut « Copie Locale » de notre référence XYZ à False, ainsi aucune copie ne sera effectué de notre DLL dans le répertoire de l’exécutable.

Maintenant que notre DLL va être incorporée au ressources de notre programme et que l’on ne généré plus une copie de cette référence, il faut charger la DLL incorporée. Et c’est la que ça commence a se « compliquer ».

Alors, premièrement rajouter un using sur System.Reflection. Ensuite, que se passe-t-il ?
Lorsque l’on fait appel à une fonction dans une DLL, la CLR va chercher à la trouver puis la charger, quand cette résolution échoue (ce qui sera le cas ici) une dernière chance nous est donné avec ce handler.
Chaque fois qu’une DLL introuvable devra être chargée, le programme exécutera le handler.

On définit tout d’abord un ressourcename. Il s’agit du nom complet d’accès a votre DLL incluant le namespace du projet.
Ici c’est MyLoader + DLL (dossier ou est le fichier). Ensuite on récupère le nom de la DLL a charger avec AssemblyName(args.Name).Name et enfin on y rajoute le suffixe .dll.
Quand il cherchera l’assembly XYZ, cela donnera : “MyLoader.DLL.XYZ.dll”
Ensuite on se charge de récupérer un flux que l’on lit et que l’on copie dans le tableau de byte AssemblyData. Ce flux correspond a notre DLL, on a donc la le contenu. Enfin on se charge de la retourner chargée en faisait le Load().
Au final notre handler aura été recherché notre DLL, lu en binaire le fichier puis chargé en mémoire et retourné a l’appelant l’assembly qui pourra être utilisé par la suite.

Notre output de compilation ne comportera que notre exe avec la dll embarqué dedans a la manière de librairie statiques.

Inform@tiquement
Istace Emmanuel

Sources :
http://blogs.msdn.com/b/microsoft_press/archive/2010/02/03/jeffrey-richter-excerpt-2-from-clr-via-c-third-edition.aspx
CLR 3 via C# (Microsoft Press, 2010; ISBN: 9780735627048), Chap. 23 – “Assembly Loading and Reflection,”