C# Academy · Oppitunti

Laajennusmetodien ratkaiseminen

Ymmärtäkää, miten kääntäjä löytää laajennukset.

Oppitunti 3/413 vaihetta

Laajennusmetodien ratkaiseminen on ilmainen C# Academy-oppitunti CoddyKitissä. Tämä on oppitunti 3/4. Voit lukea koko oppitunnin alta ilmaiseksi ja harjoitella sen jälkeen käytännössä selaimessa sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla. Oppitunti kuuluu C# Academy-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. C# Academy-kurssilla on yhteensä 4 oppituntia.

Miten kääntäjä löytää laajennusmetodit

Kun kirjoitatte value.Method() eikä vastaavaa instanssimetodia löydy, kääntäjä etsii laajennusmetodia. Se ottaa kuitenkin huomioon vain sellaiset laajennusmetodit, joiden sisältävä nimiavaruus on käytettävissä using-direktiivin kautta.

using-direktiivit tuovat laajennukset käyttöön

Laajennusmetodi näkyy vain, jos sen nimiavaruus tuodaan käyttöön. Ilman oikeaa using-direktiiviä metodia ei näytä olevan olemassa, vaikka se olisi käännetty kokoonpanoon.

using System;
using MyHelpers; // brings StringExtensions into scope

namespace MyHelpers
{
    public static class StringExtensions
    {
        public static string Reverse(this string s)
        {
            var arr = s.ToCharArray();
            Array.Reverse(arr);
            return new string(arr);
        }
    }
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine("hello".Reverse());
    }
}

Instanssimetodit tarkistetaan ensin

Kääntäjä ratkaisee kutsun vaiheittain: ensin se etsii vastaavaa instanssimetodia, myös perittyjen metodien joukosta. Vasta jos mikään niistä ei sovi, se etsii laajennusmetodeja.

using System;
using System.Collections.Generic;

public static class ListExt
{
    // Ignored: List<int> already has Add(int)
    public static void Add(this List<int> list, int a, int b)
        => list.Add(a + b);
}

public class Program
{
    public static void Main()
    {
        var list = new List<int>();
        list.Add(5); // the built-in instance Add wins for this signature
        Console.WriteLine(list[0]);
    }
}

Lähempänä olevia nimiavaruuksia suositaan

Jos kaksi laajennusmetodia sopii, voittaa se, jonka nimiavaruus on sisäkkäisessä hierarkiassa lähempänä. Jos kääntäjä ei pysty ratkaisemaan epäselvyyttä, syntyy käännösvirhe ja kutsu on täsmennettävä.

using System;

namespace Outer
{
    public static class A { public static string Tag(this int n) => "A" + n; }

    namespace Inner
    {
        public static class B { public static string Tag(this int n) => "B" + n; }

        public class Program
        {
            public static void Main()
            {
                // B is in the nearer (Inner) namespace, so it is preferred
                Console.WriteLine(7.Tag());
            }
        }
    }
}

Epäselvyyden ratkaiseminen staattisella kutsulla

Kun laajennusmetodit ovat ristiriidassa tai haluatte ilmaista kutsun yksiselitteisesti, kutsukaa staattista metodia suoraan käyttämällä sen täydellistä luokan nimeä. Näin ohitatte laajennusmetodien ratkaisun kokonaan.

using System;

public static class FormatExt
{
    public static string Bracket(this string s) => "[" + s + "]";
}

public class Program
{
    public static void Main()
    {
        // Explicit static call, no ambiguity possible
        Console.WriteLine(FormatExt.Bracket("x"));
    }
}

Laajennusmetodien ylikuormituksen ratkaisu

Jos useilla laajennusmetodeilla on sama nimi, tavallinen ylikuormituksen ratkaisu valitsee parhaiten sopivan metodin parametrityyppien perusteella aivan kuten tavallisessa metodien ylikuormituksessa.

using System;

public static class PrintExt
{
    public static string Describe(this int n) => "int:" + n;
    public static string Describe(this string s) => "str:" + s;
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine(42.Describe());
        Console.WriteLine("hi".Describe());
    }
}

Geneerinen tyypin päättely ratkaisun aikana

Geneeristen laajennusmetodien tapauksessa kääntäjä päättelee tyyppiargumentit vastaanottajasta ja argumenteista. Jos päättely epäonnistuu, laajennusmetodia ei oteta huomioon.

using System;
using System.Collections.Generic;

public static class SeqExt
{
    public static T FirstItem<T>(this IEnumerable<T> source)
    {
        foreach (var item in source) return item;
        throw new InvalidOperationException("empty");
    }
}

public class Program
{
    public static void Main()
    {
        var nums = new List<int> { 5, 6, 7 };
        // T inferred as int from the List<int> receiver
        Console.WriteLine(nums.FirstItem());
    }
}

global using -direktiivit ja implisiittiset tuonnit

Modernissa C#:ssa voidaan määrittää global using -direktiivejä, jolloin nimiavaruus on käytettävissä koko projektissa. Näin yhteiset laajennuskirjastot ovat kaikkialla käytettävissä ilman tuontien toistamista.

using System;

// In a real project a separate file might say: global using MyHelpers;
namespace MyHelpers
{
    public static class IntExt { public static int Twice(this int n) => n * 2; }
}

public class Program
{
    public static void Main()
    {
        // Works here because MyHelpers is referenced explicitly
        Console.WriteLine(MyHelpers.IntExt.Twice(8));
    }
}

Kun vastaanottajan tyypillä on merkitystä

Ratkaisu riippuu vastaanottajan staattisesta tyypistä, ei sen suorituksenaikaisesta tyypistä. Perustyypille määritetty laajennus toimii myös silloin, kun muuttuja sisältää johdetun tyypin ilmentymän, ellei näkyvillä ole tarkempaa laajennusmetodia.

using System;

public class Animal { }
public class Cat : Animal { }

public static class AnimalExt
{
    public static string Kind(this Animal a) => "animal";
}

public class Program
{
    public static void Main()
    {
        Animal a = new Cat();
        // Chosen by static type Animal
        Console.WriteLine(a.Kind());
    }
}

Ratkaisun ajattelumalli

Ajatelkaa järjestystä näin: (1) ensin instanssimetodit, sitten (2) tuotujen nimiavaruuksien laajennusmetodit lähimmästä alkaen ja lopuksi (3) ehdokkaiden välinen ylikuormituksen ratkaisu. Tämän järjestyksen tunteminen selittää lähes kaikki yllättävät tulokset.

using System;

public static class Ext
{
    public static string Wrap(this string s) => "<" + s + ">";
}

public class Program
{
    public static void Main()
    {
        // No instance Wrap exists, so the extension is used
        Console.WriteLine("node".Wrap());
    }
}

Kokeilkaa itse

Määritelkää laajennusmetodi nimiavaruudessa, tuokaa nimiavaruus käyttöön ja kutsukaa metodia. Kutsukaa sitten samaa metodia suoraan staattisena metodina nähdäksenne, että molemmat tavat toimivat.

using System;
using Text.Helpers;

namespace Text.Helpers
{
    public static class StringExtensions
    {
        public static int Vowels(this string s)
        {
            int count = 0;
            foreach (var c in s.ToLower())
                if ("aeiou".IndexOf(c) >= 0) count++;
            return count;
        }
    }
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine("education".Vowels());
        Console.WriteLine(Text.Helpers.StringExtensions.Vowels("education"));
    }
}

Pikatarkistus

Pohtikaa, mikä tekee laajennusmetodista näkyvän.

Kertaus

Laajennusmetodien ratkaiseminen noudattaa selkeää järjestystä.

  • Instanssimetodeja kokeillaan ensin; laajennusmetodit täydentävät niitä vain tarvittaessa.
  • Laajennusmetodi näkyy vain, jos sen nimiavaruus on käytettävissä using- tai global using -direktiivin kautta.
  • Lähempänä olevat nimiavaruudet voittavat; ratkaisemattomat tasatilanteet aiheuttavat käännösvirheen.
  • Voitte aina kutsua staattista metodia suoraan epäselvyyden ratkaisemiseksi.
  • Ratkaisu käyttää vastaanottajan staattista tyyppiä.
Aloita maksutta

Opi C# tekoälytuutorin avulla — ilmaiseksi

Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.

Kurssit
93
Oppitunnit
346

Usein kysytyt kysymykset

Onko oppitunti ”Laajennusmetodien ratkaiseminen” ilmainen?

Kyllä – oppitunnin ”Laajennusmetodien ratkaiseminen” koko tekstin voi lukea täällä verkossa ilmaiseksi. Jos haluat harjoitella interaktiivisesti sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla sekä avata koko C# Academy-kurssin, päivitä CoddyKit PROhon. C# Academy-kurssilla on yhteensä 4 oppituntia.

Mitä opin oppitunnilla ”Laajennusmetodien ratkaiseminen”?

Ymmärtäkää, miten kääntäjä löytää laajennukset. Harjoittelet C# Academy-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.

Tarvitsenko kokemusta aloittaakseni C# Academy-opiskelun?

Aiempi kokemus ei ole tarpeen. CoddyKitin C# Academy-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 3/4.

Kuinka kauan ”Laajennusmetodien ratkaiseminen”-oppitunnin suorittaminen kestää?

Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.

Voinko kirjoittaa ja suorittaa koodia tällä C# Academy-oppitunnilla?

Kyllä. Jokainen C# Academy-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.

Kaikki tämän kurssin oppitunnit

  1. Laajennusmetodien määrittely
  2. Rajapintojen ja geneeristen tyyppien laajentaminen
  3. Laajennusmetodien ratkaiseminen
  4. Hyvien laajennusten suunnittelu
← Takaisin: C# Academy