C#2.0 Welke regels bepalen method keuze voor delegate

Pagina: 1
Acties:

  • LoekD
  • Registratie: Augustus 2000
  • Laatst online: 24-01 09:34
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    delegate void MethodDelegate(Inherited param);

    class BaseType
    {
        //Method 1.
        public void Method(Inherited param)
        {}
    }
    class Inherited : BaseType
    {
        //Method 2.
        public void Method(object param)
        {}  
    }
    
    class Test
    {
        private static void Test()
        {
            Inherited inherited = new Inherited();
            MethodDelegate dlgt = new 
            MethodDelegate(inherited.Method);
            dlgt(inherited); //(hier stond null)
        }
    }


Wanneer ik de bovenstaande code compileer in 1.1 wordt (zoals verwacht) bij het aanroepen van Test() keurig method 1 op BaseType aangeroepen.

Wanneer ik compileer in 2.0 wordt de method 2 op Inherited aangeroepen.
Dat is blijkbaar een bekend verschil tussen 1.1. en 2.0.
De uitleg over het gedrag:
http://www.gotdotnet.com/...2.0/default.aspx#00000085
De uitleg over co en contra variance:
http://msdn2.microsoft.com/en-us/library/ms173174.aspx
De compiler waarschuwing:
http://msdn2.microsoft.com/en-us/library/zfd7f9sw.aspx

Allemaal leuk, maar ik kom er niet achter waarom nou voor de minder specifieke method wordt gekozen in plaats van de keurig aanwezige specifieke method.
Wat zijn de regels waarop deze beslissing wordt gedaan door de compiler?

[ Voor 1% gewijzigd door LoekD op 29-09-2006 15:16 . Reden: N.a.v. antwoord 1; Parameter object type heeft geen invloed op de compiler. ]

Hoe meer je drinkt, hoe korter je leeft, hoe minder je drinkt


  • eek
  • Registratie: Februari 2001
  • Laatst online: 06-04-2020

eek

@MagickNET

Ik denk dat hij ervoor kiest om de volgende reden:

BaseType erft van Object
Inherited erft van BaseType

Op het moment dat hij moet kiezen tussen de twee methodes en jij geeft null lijkt het me logischer dat hij eerst voor Object kiest ipv Inherited. Omdat hij van onderen zal gaan proberen zal hij eerst voor Object kiezen en later pas voor Inherited.

Skill is when luck becomes a habit.


  • LoekD
  • Registratie: Augustus 2000
  • Laatst online: 24-01 09:34
Ben er net nog achter gekomen dat het niet met het gebruik van delegates te maken heeft, maar meer met de manier waarop de compiler of runtime zijn aan te roepen method kiest.
Oh, en:
De bovenstaande gotdotnet pagina negeren, staat vol met fouten...

Hoe meer je drinkt, hoe korter je leeft, hoe minder je drinkt


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 18:54

.oisyn

Moderator Devschuur®

Demotivational Speaker

eek schreef op vrijdag 29 september 2006 @ 14:47:
Ik denk dat hij ervoor kiest om de volgende reden:

BaseType erft van Object
Inherited erft van BaseType

Op het moment dat hij moet kiezen tussen de twee methodes en jij geeft null lijkt het me logischer dat hij eerst voor Object kiest ipv Inherited. Omdat hij van onderen zal gaan proberen zal hij eerst voor Object kiezen en later pas voor Inherited.
Hij geeft null aan de delegate, bij het assignen aan de delegate vindt de overload resolution al plaats dus wat hij aan de delegate meegeeft doet er niet toe. Daarnaast zou je een ambiguity error moeten krijgen als je null passt aan een geoverloadde functie. Object is dan niet ineens belangrijker dan een String oid.

LoekD: De reden is dat het compiletime type van inherited een Inherited is, en dus heeft Inherited.Method voorrang. Hetzelfde zou je krijgen als je dit zou doen:
C#:
1
inherited.Method(inherited); // calls Inherited.Method instead of BaseType.Method

Pas als er geen mogelijke overload in de meest derived class gevonden kan worden, wordt pas uitgeweken naar de base class.

De reden waarom het in C# 2.0 ineens anders gaat dan in 1.1 komt zoals je al zei door de co(ntra)variance ondersteuning door delegates. Daardoor is Inherited.Method nu ook ineens een goede kandidaat, terwijl dat eerst niet zo was (en dus uitgeweken werd naar BaseType.Method)

[ Voor 10% gewijzigd door .oisyn op 29-09-2006 18:17 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • LoekD
  • Registratie: Augustus 2000
  • Laatst online: 24-01 09:34
Ok, de inherited krijgt voorrang. Daar kan ik mee leven.
Nu het volgende:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class BaseType
{
    //Method 2.
    public virtual void Method(Inherited param){}
}
class Inherited : BaseType
{       
    //Method 1.
    public void Method(object param){}
    //Method 2.
    public void Method(BaseType param){}
    //Method 3.
    public override void Method(Inherited param){}
}
class Test
{
    public static void Test()
    {
        Inherited inherited = new Inherited();
        inherited.Method(inherited);
    }
}


We roepen weer de Test method aan. Welke method wordt aangeroepen? Nee, niet Method 3, maar method 2!
Pas wanneer method 3 een 'new' krijgt ipv override, wordt deze aangeroepen.

Heeft iemand daar ook een verklaring voor?

Hoe meer je drinkt, hoe korter je leeft, hoe minder je drinkt


  • MTWZZ
  • Registratie: Mei 2000
  • Laatst online: 13-08-2021

MTWZZ

One life, live it!

Volgens mij heeft dit er mee te maken dat de CLR probeert een overload te vinden voor het directe base type voor Inherited (wat dus BaseType) is.

Het wordt nog vreemder als je dit doet:
[code=c#]
using System;
public class Foo {
}
public class BaseType
{
//Method 2.
public virtual void Method(Inherited param)
{
Console.WriteLine("BaseType.Method(Inherited)");
}

public virtual void Method(Foo param)
{
Console.WriteLine("BaseType.Method(Foo)");
}
}
public class Inherited : BaseType
{
//Method 1.
public void Method(object param)
{
Console.WriteLine("Inherited.Method(object) => " + param.GetType().ToString());
}
//Method 2.
public void Method(BaseType param)
{
Console.WriteLine("Inherited.Method(BaseType)");
}
//Method 3.
public override void Method(Inherited param)
{
Console.WriteLine("Inherited.Method(Inherited)");
}

public override void Method(Foo param)
{
Console.WriteLine("Inherited.Method(Foo)");
}
}
public class Test
{
public static void TestMethod()
{
Inherited inherited = new Inherited();
Foo f = new Foo();
inherited.Method(f);
}

public static void Main(string[] args)
{
Test.TestMethod();
}
}
[/code]
Als je dit runt komt er dit uit:
[cmd]
Inherited.Method(object) => Foo
[/cmd]

Vrij vaag IMHO


Weg met de troep hierboven :X

Na wat geexperimenteer blijkt het te zitten in de niet virtuele Methods voor object en BaseType op de Inherited klasse. De CLR heeft voorkeur voor non-override methods bij het resolven. Als je Foo ook van BaseType laat inheriten en daar alleen de overrides op definieert zie je dat het wel goed gaat als je Foo.Method(inherited) doet.

[ Voor 11% gewijzigd door MTWZZ op 02-10-2006 14:28 ]

Nu met Land Rover Series 3 en Defender 90


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 18:54

.oisyn

Moderator Devschuur®

Demotivational Speaker

Waarom nog vreemder, je demonstreert exact dezelfde situatie, alleen dan met een ander type :). Blijkbaar vindt de compiler dat een overridden method niet bij de class hoort waarin hij geoverride wordt. Best krom als je het mij vraagt - vooral raar dat er niets over te vinden is in de docs.

[ Voor 51% gewijzigd door .oisyn op 02-10-2006 14:29 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • MTWZZ
  • Registratie: Mei 2000
  • Laatst online: 13-08-2021

MTWZZ

One life, live it!

Ja inderdaad, ik ben nog even google aan het ondervragen waarom dat zo is maar tot nu toe niet veel meer dan dat er op een aantal blogs (hier en hier) iets over versioning geroepen wordt maar volgens mij is dat het ook niet helemaal.
Vb schijnt er gewoon lak aan te hebben en probeert de best-fit te vinden.

Nu met Land Rover Series 3 en Defender 90


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 18:54

.oisyn

Moderator Devschuur®

Demotivational Speaker

Oh, 't staat er wel in (7.5.5.1)
For a method invocation, the primary-expression of the invocation-expression must be a method group. The method group identifies the one method to invoke or the set of overloaded methods from which to choose a specific method to invoke. In the latter case, determination of the specific method to invoke is based on the context provided by the types of the arguments in the argument-list.

The compile-time processing of a method invocation of the form M(A), where M is a method group and A is an optional argument-list, consists of the following steps:
  • The set of candidate methods for the method invocation is constructed. Starting with the set of methods associated with M, which were found by a previous member lookup (Section 7.3), the set is reduced to those methods that are applicable with respect to the argument list A. The set reduction consists of applying the following rules to each method T.N in the set, where T is the type in which the method N is declared:
    • If N is not applicable with respect to A (Section 7.4.2.1), then N is removed from the set.
    • If N is applicable with respect to A (Section 7.4.2.1), then all methods declared in a base type of T are removed from the set.
  • If the resulting set of candidate methods is empty, then no applicable methods exist, and a compile-time error occurs. If the candidate methods are not all declared in the same type, the method invocation is ambiguous, and a compile-time error occurs (this latter situation can only occur for an invocation of a method in an interface that has multiple direct base interfaces, as described in Section 13.2.5).
  • The best method of the set of candidate methods is identified using the overload resolution rules of Section 7.4.2. If a single best method cannot be identified, the method invocation is ambiguous, and a compile-time error occurs.
  • Given a best method, the invocation of the method is validated in the context of the method group: If the best method is a static method, the method group must have resulted from a simple-name or a member-access through a type. If the best method is an instance method, the method group must have resulted from a simple-name, a member-access through a variable or value, or a base-access. If neither requirement is true, a compile-time error occurs.
Once a method has been selected and validated at compile-time by the above steps, the actual run-time invocation is processed according to the rules of function member invocation described in Section 7.4.3.

The intuitive effect of the resolution rules described above is as follows: To locate the particular method invoked by a method invocation, start with the type indicated by the method invocation and proceed up the inheritance chain until at least one applicable, accessible, non-override method declaration is found. Then perform overload resolution on the set of applicable, accessible, non-override methods declared in that type and invoke the method thus selected.
Overridden methods worden dus nooit meegenomen bij het maken van een kandidatenlijst.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • MTWZZ
  • Registratie: Mei 2000
  • Laatst online: 13-08-2021

MTWZZ

One life, live it!

Check, ik vind net hetzelfde op een iets andere plek paragraaf 7.4.2
For example, the set of candidates for a method invocation does not include methods marked override (Section 7.3), and methods in a base class are not candidates if any method in a derived class is applicable (Section 7.5.5.1).

[ Voor 40% gewijzigd door MTWZZ op 02-10-2006 15:08 ]

Nu met Land Rover Series 3 en Defender 90


  • LoekD
  • Registratie: Augustus 2000
  • Laatst online: 24-01 09:34
Zo vormt dit inderdaad een sluitende verklaring.
De method 2 uit vb 1 wordt niet gezien als geldige method in 1.1.
En omdat in 2.0 de overervende partij voorrang heeft EN contravariance (method parameter type is base van delegate parameter-type) bestaat is het duidelijk.
Bedankt voor de hulp. :)

Hoe meer je drinkt, hoe korter je leeft, hoe minder je drinkt


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 18:54

.oisyn

Moderator Devschuur®

Demotivational Speaker

In 1.1 had de overervende partij ook al voorrang hoor :). De quote die ik deed kwam uit de docs van 1.1. Het verschil was dat er in 1.1 geen geldige kandidaten waren in Inherited, waardoor uitgeweken werd naar de methods van BaseType. Maar met de contravariance dat in 2.0 geintroduceerd werd was Inherited.Method ineens wél een goede kandidaat, waardoor die automatisch gekozen werd.

[ Voor 57% gewijzigd door .oisyn op 02-10-2006 17:00 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.

Pagina: 1