Tentaliknande frågor från Föreläsningar Flashcards

1
Q
  • Konceptet: generic type
class Pair<T> {
    public T Item1 { get; set; }
    public T Item2 { get; set; }
    public Pair(T item1, T item2) {
        Item1 = item1;
        Item2 = item2;
    }
}

Vilka rader kompilerar?

1. var p1 = new Pair<int, bool>(1, true);

2. Pair<T> p2 = new Pair<T>();

3. Pair p3 = new Pair(10, 20);

4. Pair<bool> p4 = new Pair<bool>(true, true);


A

Rättsvar: 4.

Pair<bool> p4 = new Pair<bool>(true, true);


I den här raden försöker du skapa en instans av Pair<bool> korrekt genom att ange typparametern som bool. Denna rad kommer att kompilera korrekt om du vill skapa en Pair<bool>-instans.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q
  • Koncept: Generics och Typfel
class Pair<T>
{
    public T Item1 { get; set; }
    public T Item2 { get; set; }
}

var pair = new Pair<int, bool> { Item1 = 42, Item2 = true };
int second = pair.Item2;

Vilket alternativ är korrekt?
1. Kompilerar och körs.
2. Kompilerar men kastar exception. .
3. Kompilerar inte.

A

Rättsvar: Alternativ 3 är korrekt.

  • Motivering:
 När du försöker använda Pair<int, bool>, måste du ange två typer, men klassen Pair är konstruerad för att ha en och samma typ. Därför kommer det att orsaka ett kompileringsfel.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q
  • Koncept: Generic interface
interface ISequence<T>
{
T Next();
}
class NaturalSequence : ISequence<int>
{
    int current = 0;
    public int Next()
        => current++;
}
- Vilka rader kompilerar?

1.  NaturalSequence seq1 = new NaturalSequence(); 

2. ISequence<int> seq2 = new NaturalSequence();

3. ISequence<char> seq3 = new NaturalSequence();
A
  • Rättsvar: 1 och 2 kompilerar.

Motivering:

  1. Kompilerar:

Här skapar du en instans av NaturalSequence och tilldelar den till seq1, vilket är en korrekt typmatchning.
Eftersom NaturalSequence implementerar ISequence<int>, kan du tilldela en instans av NaturalSequence till en variabel deklarerad som NaturalSequence. Detta är känt som en implicit typkonvertering.</int>

  1. Kompilerar

Här skapar du också en instans av NaturalSequence och tilldelar den till seq2, som är deklarerad som en ISequence<int>.
Eftersom NaturalSequence implementerar ISequence<int>, kan du tilldela den till en variabel som har den typen. Detta möjliggör att du använder polymorfism för att hantera NaturalSequence som en ISequence<int>.</int></int></int>

  1. Kompilerar inte:

Även om NaturalSequence implementerar ISequence<int>, kan du inte tilldela en instans av NaturalSequence till en variabel av en annan generisk typparameter, som ISequence<char>.
Generiska typer är typsäkra, och de måste matcha exakt. I det här fallet måste typen matcha ISequence<int> om du vill tilldela en instans av NaturalSequence till det.</int></char></int>

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

Koncept: Generic interface

interface ISequence<T> {
T Next(); 
}
class Alphabet : ISequence<char> 
{
    int i = -1;
    char[] letters = { 'A', 'B', 'C', /*...*/  'Z' 
};

  
    public char Next()
        => letters[i = (i + 1) % letters.Length];
				
}
  • Vilka rader kompilerar?
1. Alphabet seq1 = new Alphabet();   
     
2. ISequence<int> seq2 = new Alphabet();

3. ISequence<char> seq3 = new Alphabet();
A
  • Rättsvar: 1 och 3 kompilerar.

Motivering:

  1. Kompilerar:

Eftersom du skapar en instans av Alphabet och tilldelar den till en variabel av samma typ, Alphabet.

  1. Kompilerar inte:

Även om Alphabet implementerar ISequence< char >, försöker du tilldela en instans av Alphabet till en variabel av typen ISequence< int >. Generiska typer måste matcha exakt, och ISequence< int > och ISequence< char > är inte kompatibla eftersom de har olika generiska typparametrar.

  1. Kompilerar:

Denna rad kommer att kompilera eftersom Alphabet implementerar ISequence< char >, och du tilldelar instansen av Alphabet till en variabel av samma typ, ISequence< char >.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

Koncept: Varians

class Fruit { }
class Apple : Fruit { }
Fruit fruit = new Apple();

IEnumerable<Fruit> fruits = new List<Apple>();

Är en lista av äpplen en lista av frukt?

A

Ja, när det kommer till IEnumerable och andra kovarianta gränssnitt, kan en lista av äpplen (List< Apple>) behandlas som en lista av frukt (IEnumerable< Fruit>) om Apple är en subtyp av Fruit.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

Är detta ett öppet eller stängt arv av en generic interface?

interface ISequence<T>
{
T Next();
}
interface ICharSequence : ISequence<char>
{
    void SetUpperCase(); // Changes to uppercase.
    void SetLowerCase(); // Changes to lowercase.
}

A

Rättsvar: Stängd.

Motivering:

I det här fallet har du stängt den generiska typen T till char genom att använda ISequence<char> som den generiska typen. Nu är ICharSequence specifikt för tecken (char) och inte längre generiskt.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

Är detta ett öppet eller stängt arv av en generic interface?

iinterface ISequence<T>
{
T Next();
}
interface ICycle<T> : ISequence<T>
{
    void Reset();
}
A

Rättsvar: Öppet

Har finns ingen typbegränsning som t.ex. char eller int.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

Koncept: Öppen constructed generic interface.

class Pair<T1, T2> 
{
    public T1 Item1 { get; init; }
    public T2 Item2 { get; init; }
    public Pair(T1 item1, T2 item2) {
        Item1 = item1;
        Item2 = item2;
    }
}


class MonoPair<T> : Pair<T, T> 
{
    public MonoPair(T item1, T item2) : base(item1, item2) { }
}

Vilka rader kompilerar?

1. MonoPair<int> pair1 = new MonoPair<int>(2, 3);

2. Pair<char, char> pair2 = new MonoPair<char>('A', 'B');

3. Pair<string> pair3 = new MonoPair<string>("hello", "world");
A

Rättsvar: 1 och 2 kompilerar.

  1. Kompilerar:

Du skapar en instans av MonoPair< int > och tilldelar den till en variabel av samma typ, MonoPair< int >, vilket är en korrekt användning av den generiska typen.

  1. Kompilerar:

Du skapar en instans av MonoPair< char > och tilldelar den till en variabel av typen Pair< char, char >. Eftersom MonoPair< T > ärver från Pair< T, T >, kan du använda en instans av MonoPair< T > som en instans av Pair< T1, T2 >.

  1. Kompilerar inte:

Du försöker skapa en instans av MonoPair< string > och tilldela den till en variabel av typen Pair< string >. Eftersom MonoPair< T > har två generiska typparametrar och Pair< string > har bara en, är de inte kompatibla, och denna rad kommer att orsaka ett kompileringsfel.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

Anta att:


Apple : Fruit
Fruit : Edible 
T är kovariant i  I<T> .
Vilka påståenden stämmer?
1. I<Apple> : I<Fruit>
2. I<Fruit> : I<Apple>
3. I<Apple> : I<Edible>
4. I<Edible> : I<Fruit>
A

Rättsvar: 1 och 3 stämmer.

Motivering:

1. Stämmer: <Apple> är en mer specifik typ än <Fruit>, så du kan använda kovarians för att tilldela en instans av <Apple> till <Fruit>. Detta är tillåtet eftersom Apple ärver från Fruit.

2. Stämmer inte: Eftersom kovarianstypning tillåter dig att tilldela en mer specifik typ till en mer generell typ, kan du inte tilldela <Fruit> till <Apple> eftersom det skulle vara kontravariant. Kovarianstypning fungerar endast i riktning från mer specifikt till mer generellt.

3. Stämmer: Eftersom Edible är en överordnad typ till Fruit, kan du använda kovarians för att tilldela en instans av <Apple> till <Edible>. Om T är kovariant i <T>, kan du gå upp i hierarkin av ärftliga typer.

 4. Stämmer inte: Eftersom Edible är överordnad Fruit, kan du inte använda kovarians för att tilldela <Edible> till <Fruit>. Kovarianstypning fungerar endast i riktning från mer specifikt till mer generellt.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

Koncept: Generic interface med constraint.

public class Inventory<T> where T : IItem
{
private List<T> items = new List<T>();
private int currentIndex = 0;

public string CurrentName
    => CurrentItem.Name;

public T CurrentItem
    => items[currentIndex];

public void Add(T item)
    => items.Add(item);

public void Next()
    => currentIndex  = (currentIndex + 1) % items.Count;
}

Vilka av följande påståenden stämmer om varför detta constraint behövs?

  1. Säkerhet:

Denna constraint begränsar användningen av generiska typer till de som uppfyller vissa krav, vilket ökar säkerheten i din kod.

  1. Metodåtkomst:

Genom att kräva att generiska typer implementerar ett gränssnitt (eller har vissa egenskaper/metoder), kan du vara säker på att du kan använda dessa metoder i din generiska klass.

3: Typtillförlitlighet:

Denna constraint hjälper till att säkerställa att generiska typer är av de förväntade typerna eller implementerar de krav som behövs, vilket ökar typtillförlitligheten i din kod.

A

Rättsvar: Samtliga stämmer.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q
class Fruit { }
class Apple : Fruit { }
class AppleJuicer
{
public virtual void Juice(Apple apple)
=> Console.WriteLine("Turning apple into juice");
}
class FruitJuicer : AppleJuicer
{
public override void Juice(Fruit fruit)
=> Console.WriteLine("Turning fruit into juice");
  • Vilka påståenden stämmer:
  1. FruitJuicer är typ-säker.
  2. AppleJuicer är typ-säker.
  3. Varken FruitJuicer eller AppleJuicer är typ-säkra.
A

Rättsvar: 3 stämmer.

Motivering:

I det givna exemplet är varken FruitJuicer eller AppleJuicer typsäkra. För att vara typsäker måste de överskuggade metoderna ha identiska signaturer.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q
class Player1
{
bool has DoubleJumpPowerUp = false;
public void MoveRight()=> Console.WriteLine("Moving right.");

public void Jump()
{
if (!hasDoubleJumpPowerUp)
    Console.WriteLine("Jumping");
else
Console.WriteLine("Double jumping");
}

Svara på följande frågor om lösningen ovan som inte implemetarar dependensy injection:

  • Varför är det problematiskt att ha hårdkodade variabler som hasDoubleJumpPowerUp i koden?
  • Hur påverkar bristen på abstraktion i koden hanteringen av olika typer av power-ups?
  • Varför är det en utmaning att underhålla koden när antalet conditionals ökar för att hantera olika typer av power-ups?
A
  • Hårdkodade variabler:

Du har hårdkodat hasDoubleJumpPowerUp som en instansvariabel i Player1. Detta innebär att du skulle behöva ändra koden direkt i klassen varje gång du vill lägga till eller ta bort en ny typ av power-up. Detta är varken skalbart eller underhållbart, särskilt om du har många olika typer av power-ups.

  • Brist på abstraktion:

Koden har ingen mekanism för att hantera olika typer av power-ups. Om du vill lägga till fler typer av power-ups i framtiden, måste du sannolikt ändra koden i Player1, vilket bryter mot öppen/sluten principen.

  • Många conditionals:

Om du skulle lägga till fler power-ups, skulle du behöva lägga till fler villkor för att hantera varje typ av power-up. Detta kan bli rörigt och svårt att underhålla över tiden.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

Anta att:

Apple : Fruit
Fruit : Edible
T är kontravariant i I< T > .

Vilka påståenden stämmer?

1. I<Apple> : I<Fruit>
2. I<Fruit> : I<Apple>
3. I<Apple> : I<Edible>
4. I<Edible> : I<Fruit>
A

Svar: 2 och 4

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

Koncept: Komposition över Arv

abstract class Duck
{
    public virtual string Quack() => "Quack!";
    public virtual string Swim() => "Swim!";
}
class MallardDuck : Duck
{
}
// Andra metoder relaterade till Mallard...
class RedheadDuck : Duck
{
}

Vad är problemet som uppstår om skulle vilja lägga till en RubberDuck?

class RubberDuck : Duck
{
    public override string Quack() => "Squeak!";
    public override string Swim() => "Floats.";
}
A

Problemet som uppstår när du lägger till RubberDuck klassen är att den ärver från Duck, och det finns en konflikt mellan den naturliga beteendet som Duck har definierat och det specifika beteendet som RubberDuck försöker införa.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q
}
string PositiveOrNegative(int number) => number > 0 ? "Positive" : "Negative";

string ToHex(int number)
=> number.ToString("X");
string FizzBuzz(int number)
{ 
if (number % 15 == 0)
return "FizzBuzz";

else if (number % 3 == 0)
return "Fizz";

else if (number % 5 == 0)
return "Buzz";

else return number.ToString();
}

Vad har alla dessa gemensamt?

A

Du får klura vidare på den ;)

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

Koncept: funktions delegat

public class Player 
{
JumpBehavior jumpBehavior;

public Player(JumpBehavior behavior)
=> jumpBehavior = behavior;

public void SetJumpBehavior (JumpBehavior behavior)
=> jumpBehavior = behavior;

public void Jump()
=> jumpBehavior();
}

Vilken rad är korrekt?

  1. player.Jump();
  2. Player jumpBehavior = new Player(Jumping);
A

Rättsvar: 1 är rätt svar player.Jump();

Motivering:

Här anropar du metoden Jump() på en befintlig instans av Player. Detta kommer att använda den aktuella jumpBehavior som har satts tidigare och utföra hoppbeteendet som är associerat med den instansen.

Varför använda Funktions delegat?
Eftersom man kan ändra beteendet hos en instans av Player genom att bara byta ut den refererade metoden, vilket ger stor flexibilitet och dynamik.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
17
Q

Koncept: Funktions delegat

delegate int Operation(int value);

int Dec (int x) => x - 1;

int Add (int x, int y) => x + y;

int Inv (int x) => -x;
  • Vilka rader Kompilerar?
  1. Operation op1 = Dec;
  2. Operation op2 = Add;
  3. Operation op3 = Inv;
A

Rättsvar: 1 och 3 kompilerar.

Motivering:

  1. Kompilerar:
    Du tilldelar Dec-metoden till op1, och eftersom metoden Dec har samma signatur som Operation, kan du använda den i en Operation-delegat.
  2. Kompilerar inte:
    Även om Add-metoden har samma returtyp som Operation, har den en annan parametertyp (två int-parametrar istället för en). Detta innebär att Add inte kan tilldelas en Operation-delegat direkt.
  3. Kompilerar:
    Inv-metoden har samma signatur som Operation med en int-parametrar och en int-returtyp.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
18
Q

Kan alla typer och/eller metoder vara generiska?

  1. Ja, dock är generiska typer och metoder mindre snabba än icke-generiska metoder.
  2. Ja, alla typer och metoder kan vara generiska.
  3. Nej, kod som gör antaganden om underliggande typen kan inte vara generisk.
A
  1. Nej, kod som gör antaganden om underliggande typen kan inte vara generisk.

Exempel på antaganden som inte fungerar:
+ , - , > , == , != , .Name , .Start() , etc

19
Q

Vilka/vilken av dessa metoder kan vara generisk?

public class Pair<T>
{
 public T First { get; set; }
 public T Second { get; set; }

 public T Combine () => First + Second;

 public void Flip ()
  {
  T temp = First;
  First = Second;
  Second = temp;
   }
}
  1. T Combine ( )
  2. Flip ( )
  3. Båda (Flip och Combine)
  4. Ingen
A

Rättsvar: Alternativ 2 är rätt.

Motivering:

I det givna exemplet kan inte Combine()-metoden vara generisk. Orsaken är att de båda metoderna använder operatören + på First och Second, vilket innebär att de antar att typen T har operatorer som stöder addition.

20
Q

Koncept: Built-in delegate

bool IsOdd(int x) => x % 2 != 0;
bool ContainsE(string str) => str.Contains("e");
Predicate<int> isOdd = IsOdd;
Predicate<string> containsE = ContainsE;

Console.WriteLine(isOdd(7));
Console.WriteLine(containsE("Elephant"));
  • Vilket är resultatet av containsE(“Elephant”)?
  1. true
  2. false
  3. Elephant
  4. 7
A
  • Rättsvar: true

Motivering:
Funktionen ContainsE returnerar true för det givna argumentet “Elephant” eftersom strängen innehåller bokstaven “e”.

21
Q

Koncept: Built-in delegate

bool IsUpper(char c) => Char.IsUpper(c);
Func<char, bool> func = IsUpper;
Predicate<char> pred = IsUpper;

func = pred; 
pred = func; 

Vad stämmer:

  1. Koden leder till Kompileringsfel.
  2. Vi konverterar func till pred och pred till func.
A

Rättsvar: 1 leder till kompileringsfel.

Motivering:

Eftersom de är olika typer av delegater kan de inte direkt konverteras till varandra, och därför leder försöket att tilldela func till pred eller vice versa till ett kompileringsfel.

22
Q

Koncept: Built-in delegate

bool IsUpper(char c) => Char.IsUpper(c);

bool IsLower(char c) => Char.IsLower(c);

Predicate<char> isUpper = IsUpper;

Predicate<char> isLower = IsLower;

Predicate<char> combined = c => isUpper(c) || isLower(c);

Console.WriteLine(combined('A'));
  • Vad skrivs ut?
  1. False
  2. True
A

Rättsvar: True.

23
Q

Koncept: Built-in delegate

void PrintLine() => Console.WriteLine("===");
Action printDivider = PrintLine;

printDivider += PrintLine;  

printDivider -= PrintLine;  

printDivider += PrintLine; 

printDivider();
  • Vad skrivs ut?
  1. 3 rader skrivs ut.
  2. 1 rad skrivs ut.
A

Rättsvar: 1 rad.

Motivering:

  1. printDivider += PrintLine; - Lägger till PrintLine i delegaten.
  2. printDivider -= PrintLine; - Tar bort PrintLine från delegaten.
  3. printDivider += PrintLine; - Lägger till PrintLine i delegaten igen.

Så när du anropar printDivider() kommer det bara att utföra PrintLine en gång, och därför kommer 1 rad att skrivas ut.

24
Q

Koncept: Built-in delegate (Actions med exeptions)

void Good() => Console.WriteLine("This method runs!");
void Bad() => throw new Exception("This method throws!");
Action combined = (Action)Good + Bad + Good;
combined();

Vad kommer skrivs ut?

  1. This method runs!
  2. This method runs! + This method throws!
  3. This method throws!
A

Rättsvar: Är 2.

Motivering:
Eftersom metoden Bad kastar ett undantag (Exception) när den körs, kommer det att generera ett undantag med meddelandet “This method throws!”. Därför kommer Bad-metoden att avbryta utförandet av resten av operationerna och resultera i att “This method runs!” skrivs ut en gång och sedan “This method throws!”.

24
Q

Koncept: Lambda

var add = (int a, int b) => a + b;

var isPositive = (int x)=> x > 0;

var line = () => { Console.WriteLine("----"); };
  • Vilken typ ersätter var för add?
  • Vilken typ ersätter var för isPositive?
  • Vilken typ ersätter var för line?
A

Rättsvar:

  • Typen som ersätter var för add är Func<int, int, int>.
  • Typen som ersätter var för isPositive är Func<int, bool>.
  • Typen som ersätter var för line är Action.

Dessa är de delegattyper som används för att definiera lambda-uttrycken i respektive variabler.

25
Q

Koncept: Lambda

int intAdd1 (int x) => x + 1;   
  
var intAdd2 = (int x) => x + 1;

Vilket av följande påståenden är korrekt/a när det gäller de två funktionerna intAdd1 och intAdd2?

  1. Båda är liknande funktioner, men skillnaden är att intAdd2 är definierad som ett lambda-uttryck och använder var.
  2. intAdd2 är ett lambda-uttryck medan intAdd1 är en lokal funktion, och skillnaden är att intAdd2 använder var och har en något mer kompakt syntax.
  3. Båda är liknande funktioner, men skillnaden är att intAdd1 är en lambda-funktion som tar emot vilken typ som helst som indata, medan intAdd2 är mer specifik och tar bara emot heltal.
A

Rättsvar: Är 1 och 2.

Motivering:
intAdd2 är ett lambda-uttryck medan intAdd1 är en lokal funktion, och skillnaden är att intAdd2 använder var och har en något mer kompakt syntax.

Båda funktionerna utför dock samma operation, det vill säga att de ökar värdet av x med 1.

26
Q

Koncept: Lambda och typ-inferens

var isNegativeExplicit = bool (int x) => x < 0; 
var isNegativeInferred1 = (int x) => x < 0;
var isNegativeInferred2 = bool (x) => x < 0;
var isNegativeInferred3 = x => x < 0;
  • Vilka rader kompilerar?
  1. isNegativeInferred1 och isNegativeInferred3.
  2. Alla rader.
  3. isNegativeInferred1 och isNegativeExplicit.
A

Rättsvar: 3 är rätt.

Motivering:

Det vill säga både isNegativeInferred1 och isNegativeExplicit är korrekta användningar av lambda-uttryck medan isNegativeInferred2 och isNegativeInferred3 är ogiltiga.

isNegativeInferred2 försöker explicit ange typen bool för lambda-parametern x, och isNegativeInferred3 är en ogiltig syntax när typen inte anges.

27
Q

Koncept: Observer pattern

Interface IObserver 
{ 
void Update(Podcast pod); 
}
class Podcast {

public string Title { get; set; }
public string CurrentEpisode { get; set; }

List<IObserver> observers = new();

public void Add(IObserver observer)
=> observers.Add(observer);

public void ReleaseNewEpisode(string title)
{ 
CurrentEpisode = title;
foreach (IObserver observer in observers)
observer.Update(this);
}
}
class Subscriber : IObserver {
public void Update(Podcast pod)
=> Console.WriteLine($"{pod.Title}: {pod.CurrentEpisode}");
}

Vad är huvudsyftet med Observer-mönstret i koden?

  1. Hantera undantag i programmet.
  2. Implementera arv och polymorfism.
  3. Tillåta en objekt att meddela sina ändringar till flera andra objekt utan att de är direkt kopplade till varandra.
  4. Kontrollera flödet av programmet.
A

Rättsvar: 3 är rätt.

28
Q

Koncept: Observer pattern

Interface IObserver { 
void Update(Podcast pod); 
}
class Podcast {

public string Title { get; set; }

public string CurrentEpisode { get; set; }

List<IObserver> observers = new();

public void Add(IObserver observer)
=> observers.Add(observer);

public void ReleaseNewEpisode(string title)
{ CurrentEpisode = title;

foreach (IObserver observer in observers)
observer.Update(this);
}
}
class Subscriber : IObserver {
public void Update(Podcast pod)
=> Console.WriteLine($"{pod.Title}: {pod.CurrentEpisode}");
}

Vilken typ är IObserver och vad är dess roll i detta mönster?

  1. IObserver är en klass som representerar en podcast.
  2. IObserver är en abstrakt klass som inte används i mönstret.
  3. IObserver är en typ för att lagra data om podcast-avsnitt.
  4. IObserver är ett gränssnitt som används av klasser som vill fungera som observatörer och få uppdateringar från Podcast-objektet.
A

Rättsvar: 4

29
Q

Koncept: Observer pattern

Interface IObserver { 
void Update(Podcast pod); 
}
class Podcast {

public string Title { get; set; }

public string CurrentEpisode { get; set; }

List<IObserver> observers = new();

public void Add(IObserver observer)
=> observers.Add(observer);

public void ReleaseNewEpisode(string title)
{ CurrentEpisode = title;

foreach (IObserver observer in observers)
observer.Update(this);
}
}
class Subscriber : IObserver {
public void Update(Podcast pod)
=> Console.WriteLine($"{pod.Title}: 
{pod.CurrentEpisode}");
}
  • Kan du identifiera hur Subscriber-klassen fungerar som en observatör i detta mönster?
  1. Subscriber-klassen är en underordnad klass till Podcast och kan inte vara en observatör.
  2. Subscriber-klassen är en hjälpklass som används för att lagra prenumerantinformation.
  3. Subscriber-klassen implementerar IObserver-gränssnittet och har en Update-metod som skriver ut information om det aktuella podcast-avsnittet när det är tillgängligt.
  4. Subscriber-klassen är orelaterad till observer pattern och har ingen funktion i mönstret.
A

Rättsvar: 3.

30
Q

Vad är huvudsyftet med Iterator-Pattern?

  1. Att skapa en uppsättning samlingar.
  2. Att tillhandahålla ett sätt att accessa elementen i en samling sekventiellt utan att avslöja dess underliggande struktur.
  3. Att implementera ett gränssnitt för att jämföra samlingar.
A

Rättsvar: Alternativ 2.

31
Q

Koncept: Iterator pattern.

class MyCollection<T> : IEnumerable<T>
{
    private List<T> items = new List<T>();

    public void Add(T item)
    {
        items.Add(item);
    }

    public IEnumerator<T> GetEnumerator()
    {
        return items.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

{
MyCollection<int> collection = new MyCollection<int>();
        collection.Add(1);
        collection.Add(2);
        collection.Add(3);

        Console.WriteLine("Elements in the collection:");
        foreach (int item in collection)
        {
            Console.WriteLine(item);
        }
  • Hur används Iterator-mönstret i detta exempel?
  1. Iterator-mönstret används för att skapa en komplex datastruktur.
  2. Iterator-mönstret används för att tillåta en sekventiell åtkomst av samlingselementen genom att implementera IEnumerable och IEnumerator.
  3. Iterator-mönstret används för att generera slumpmässiga nummer.
A

Rättsvar: Alt 2.

Motevering:
Iterator-mönstret används för att möjliggöra sekventiell åtkomst av element i en samling utan att avslöja den underliggande strukturen. I det här exemplet har MyCollection-klassen implementerat både IEnumerable< T> och IEnumerator< T>. Detta gör det möjligt att använda foreach-loopen för att iterera över elementen i samlingen på ett sekventiellt sätt.

32
Q

Koncept: Enumerables.

// Skapa en lista med heltal.
List<int> numbers = new List<int>() { 10, 20, 30 };
// Hämta en enumerator från listan.
IEnumerator<int> enumerator = numbers.GetEnumerator();

// Anropa MoveNext för att gå till det första elementet.
if (enumerator.MoveNext())
{
    // Nuvarande element är tillgängligt efter att MoveNext har anropats.
    Console.WriteLine(enumerator.Current); // 
}

Vilket av dessa alternativ är korrekt?
1. Koden genererar ett undantag eftersom enumerator.Current kallas innan MoveNext har anropats.
2. Koden kommer att skriva ut “10” eftersom MoveNext anropas innan enumerator.Current.
3. Koden kommer att skriva ut “0” eftersom enumerator.Current returnerar 0 om det inte finns något aktuellt element.

A

Rättsvar: Alternativ 2.

Motivering:
För att få tillgång till det nuvarande elementet i en IEnumerator måste MoveNext anropas för att gå till det första elementet i samlingen.
I detta fall anropas MoveNext, och sedan kan enumerator.Current användas för att hämta värdet 10.

33
Q
IEnumerable<int> GetInfiniteList ()
{ int i = 0;
while (true)
yield return i++;
}
  • Vilka av alternativen kompilerar?
  1. GetInfiniteList();
  2. GetInfiniteList.Take(5);
  3. foreach (int x in GetInfiniteList())
    Console.WriteLine(x)
  4. foreach (int x in GetInfiniteList().Take(5))
    Console.WriteLine(x)
A

Rättsvar: Alternativ 4.

Det genererar de första 5 värdena från den oändliga sekvensen och skriver ut dem.

34
Q

Koncept: Linq

List<int> nums = new () { 1, 2, 3 };
nums.Select(x => x * 10);
List<int> nums = new () { 100, 200, 300 };
nums.Aggregate(0, (acc, x) => acc + x);

Vad blir resultatet av dessa operationer?

  1. result1 blir { 10, 20, 30 } och result2 blir 600.
  2. result1 blir { 10, 20, 30, 40 }, och result2 blir 900
  3. result1 blir { 10, 20, 30 }, och result2 kompilerar inte.
A

Rättsvar: Alternativ 1.

Motivering:

nums.Select(x => x * 10);: Denna operation projicerar varje element i listan nums genom att multiplicera dem med 10. Resultatet blir en sekvens med värden {10, 20, 30}. Därför är alternativ 1 korrekt för result1.

nums.Aggregate(0, (acc, x) => acc + x);: Denna operation använder Aggregate för att summera alla värden i listan nums med en initial ackumulatorvärde på 0. Resultatet blir 600. Därför är alternativ 1 korrekt för result2.

35
Q

Koncept: Kontravarians

Anta att:
Apple : Fruit
Fruit : Edible
T är kontravariant i <T> .
  • Vilka påståenden stämmer?
1. <Apple> : <Fruit>
2. <Fruit> : <Apple>
3. <Apple> : <Edible>
4. <Edible> : <Fruit>
A

Rättsvar: Alternativ 1 och 4 stämmer.

Motevering:
1. Detta är sant. Eftersom Apple är en undertyp till Fruit, så är Fruit en övertyp till Apple.

36
Q

Koncept: Kovarians

Anta att T i IList<T> var kovariant.
IList<Fruit> fruits = new List<Apple>();
fruits.Add(new Orange());

- Vilka stämmer:

1.  T i IList<T> är inte kovariant.
2. Koden kompilerar.
3. Koden kastar exeption.
A

Rättsvar: Alla stämmer.

Motivering:

1.  T i IList<T> är inte kovariant: I .NET är IList<T> inte deklarerad som kovariant, så du kan inte använda det som om det vore kovariant.

2. Koden kompilerar: I koden skapar du en List<Apple> och tilldelar den till en IList<Fruit>. Detta är möjligt eftersom du kan tilldela en mer specifik typ (List<Apple>) till en mer generisk typ (IList<Fruit>) i C#, vilket kallas kovarians.

3. Koden kastar exception: Eftersom du har tilldelat en List<Apple> till fruits, kan du försöka lägga till en Orange i listan. Eftersom listan är faktiskt en List<Apple>, kommer försöket att lägga till en Orange orsaka en exception vid körning.
37
Q

Koncept: kontravariant

IList<Fruit> fruits = new List<Fruit>() {
new Orange()
};
IList<Apple> apples = fruits;
Apple first = apples[0];

Vilka stämmer:

1.  T i IList<T> är inte kontravariant.
2. Koden kompilerar.
3. Koden kastar exeption.
A

Rättsvar: Alla stämmer.

38
Q

Koncept: Varians delegates

class Fruit { }
class Apple : Fruit { }
delegate Fruit FruitFactory();
delegate Apple AppleFactory();
Apple MakeApple() => new Apple();
  • Vilken rad kompilerar nedan utan fel?
  1. AppleFactory appleFactory = MakeApple;
  2. FruitFactory fruitFactory = appleFactory;
A

Rättsvar: Alternativ 1.

39
Q
interface ISequence<T> 
{
T Next();
}

class Cycle<T> : ISequence<T> {
int i = -1;

T[] elems;
public Cycle(T[] items)=> elems = items;

public T Next() => elems[i = (i + 1) % elems.Length];
} 

Vilka rader kompilerar?

1. Cycle<int> seq1 = new Cycle<int>(new int[]{ });

2. ISequence<char> seq2 = new Cycle<char>(new char[]{ }); 

3. ISequence<T> seq3 = new Cycle<T>(new T[]{ }); 
A

Rättsvar: 1 och 2 kompilerar.

Motivering:

1. Denna rad kommer att kompilera. Här skapar du en instans av Cycle<int> och tilldelar den till en variabel seq1 av typen Cycle<int>.

2. Denna rad kommer att kompilera. Här skapar du en instans av Cycle<char> och tilldelar den till en variabel seq2 av typen ISequence<char>. Det är tillåtet att tilldela en instans av en klass som implementerar ett gränssnitt till en variabel av gränssnittets typ.

3. Denna rad kommer inte att kompilera. Anledningen är att du försöker skapa en instans av Cycle<T> och tilldela den till en variabel av typen ISequence<T>, men T är inte en känd typ vid kompileringstid. För att använda generiska typer som T måste de vara specifika vid kompileringstid.
40
Q

Ingen fråga bara en bra påminnelse:

-Tumregel-

Om subtypen är generisk
kan supertypen förbli “öppen”.
Ex:

Cycle<T> : ISequence<T>

Om subtypen är icke-generisk
måste supertypen “stängas”.
Ex:

Alphabet : ISequence<char>
A

Tack Chris <3

41
Q

Med iteration pattern kan vi undvika att exponera underliggande kollektioner. Men varför vill vi det?

A

Abstraktion och encapsulation: Genom att använda ett iteratorgränssnitt (som IEnumerable i C#) kan vi abstrahera bort komplexiteten i den underliggande datalagringen och tillhandahålla en enkel och enhetlig metod för att iterera igenom elementen. Det hjälper till att hålla koden ren och separerad från detaljerna i datalagringen.

Säkerhet och integritet: Genom att inte exponera den underliggande kollektionen direkt kan vi hantera åtkomst och manipulation av data på ett kontrollerat sätt. Det minskar risken för oavsiktlig ändring av datat eller obehörig åtkomst.

Flexibilitet och utbytbarhet: Genom att använda iterator pattern kan vi ändra den underliggande datalagringen utan att påverka koden som använder iteratorn. Det gör det enkelt att byta ut en datalagring med en annan utan att ändra mycket av koden som använder den.

Optimering: Iterator pattern möjliggör implementering av försenad (lazy) iteration. Det innebär att elementen inte behöver laddas i minnet förrän de verkligen behövs. Det sparar minnesanvändning och kan leda till bättre prestanda.
42
Q
interface IObserver
{
    void Update(Podcast pod);
}

class Podcast
{
    public string Title { get; set; }
    public int CurrentEpisode { get; set; }
    
    List<IObserver> observers;

    public Podcast()
    {
       
        observers = new List<IObserver>();
    }

    public void Add(IObserver observer)
    {
        observers.Add(observer);
    }

    public void ReleaseNewEpisode(string title)
    {
        
        CurrentEpisode = int.Parse(title);

        CurrentEpisode++;
        
        foreach (IObserver observer in observers)
            observer.Update(this);
    }
}

class Program
{
    static void Main(string[] args)
    {
        Podcast podcast = new Podcast { Title = "Alex&Sigge", CurrentEpisode = 505 };
				
        Subscriber subscriber = new Subscriber();
        podcast.Add(subscriber);

        // Simulera släppet av ett nytt avsnitt
        podcast.ReleaseNewEpisode("505");
    }
}
  • Vad händer?
  1. Följande skrivs ut: Alex&Sigge: 506.
  2. Följande skrivs ut: Alex&Sigge: 505.
  3. Det blir kompilerings fel.
A

Svar: 1. Följande skrivs ut: Alex&Sigge: 506.

Motivering: Missa inte denna raden CurrentEpisode++;