2015-01-27 15 views
15

Powiedzmy, że napisałem własną metodę odwrócenia listy w miejscu.Czy istnieje możliwość zastosowania ogólnej metody do listy pozycji?

public static void MyReverse<T>(List<T> source) 
{ 
    var length = source.Count; 
    var hLength = length/2; 
    for (var i = 0; i < hLength; i++) 
    { 
     T temp = source[i]; 
     source[i] = source[length - 1 - i]; 
     source[length - 1 - i] = temp; 
    } 
} 

Ja to tak nazywam i działa.

var fooList = new List<Foo>(); 
MyReverse(fooList); 

Jeśli chcę cofnąć wiele list, nazywam to tak.

var fooList = new List<Foo>(); 
var barList = new List<Bar>(); 
var bazList = new List<Baz>(); 
MyReverse(fooList); 
MyReverse(barList); 
MyReverse(bazList); 

Jeśli chcę, aby odwrócić dowolną liczbę list, będę próbować:

public static void Main(string[] args) 
{ 
    var lists = new List<object> 
    { 
     new List<Foo>(), 
     new List<Bar>(), 
     new List<Bar>() 
    }; 

    ReverseLists(lists); 
} 

public static void ReverseLists(List<object> sourceLists) 
{ 
    foreach (var sourceList in sourceLists) 
    { 
     MyReverse(sourceList); // Error: Type arguments cannot be inferred from usage 
    } 
} 

Ale to generuje błąd czasu kompilacji. Czy to, co próbuję zrobić, czy możliwe jest wdrożenie metody ReverseLists?

+0

'sourceList' jest' obiektem'. – leppie

+0

@Rik: Najwyraźniej to nie sprawi, że kompilator będzie szczęśliwy ... – leppie

+1

Jeśli * naprawdę * potrzebujesz tego, spróbuj z 'List ' jako typ parametru 'ReverseLists'. – Ani

Odpowiedz

10

Zakładając, że statycznej metody jak ten

public static class ReverseHelper 
{ 
    public static void MyReverse<T>(IList<T> source) 
    { 
     var length = source.Count; 
     var hLength = length/2; 
     for (var i = 0; i < hLength; i++) 
     { 
      T temp = source[i]; 
      source[i] = source[length - 1 - i]; 
      source[length - 1 - i] = temp; 
     } 
    } 
} 

za pomocą interfejsu rodzajowego i non rodzajowego klasy można to zrobić.

public interface IReverser 
{ 
    void Reverse(); 
} 
public class ListReverser<T> : IReverser 
{ 
    private readonly IList<T> source; 
    public ListReverser(IList<T> source) 
    { 
     this.source = source; 
    } 
    public void Reverse() 
    { 
     ReverseHelper.MyReverse<T>(source); 
    } 
} 
static void Main(string[] args) 
{ 
    var lists = new List<IReverser> 
    { 
     new ListReverser<Foo>(new List<Foo>()), 
     new ListReverser<Bar>(new List<Bar>()), 
     new ListReverser<Bar>(new List<Bar>()) 
    }; 

    foreach (var reverser in lists) 
    { 
     reverser.Reverse(); 
    } 
} 

Użyłem IList<T> w przeciwieństwie do List<T> wspierać szerszą liczbę typów; Jeśli chcesz List<T>, możesz go odłożyć.

+1

IMO to najtańsza opcja. – Jamiec

2

Jeśli zmienisz ReverseLists podpis metody do

public static void ReverseLists<T>(IEnumerable<object> sourceLists) 
{ 
    foreach (var sourceList in sourceLists.OfType<List<T>>()) 
    { 
     MyReverse(sourceList); 
    } 
} 

Następnie można nazwać to dla każdego typu listy:

ReverseLists<Foo>(lists); 
ReverseLists<Bar>(lists); 

Może nie idealne konieczności wzywania go raz od rodzaju listy, ale stosunkowo niewielka zmiana w istniejącym kodzie.

+0

@Nathan: Ah - teraz mam: Wywołujesz 'ReverseLists ' na tej samej liście dla wszystkich możliwych typów i każdy "run", który jest odwrócony. To * działałoby *, ale musielibyśmy go uruchomić dla każdego typu: Co, jeśli istnieje wiele różnych typów na liście nie tylko 2 - tam zaczynałoby się robić bałagan ... – ChrFin

+0

@ChrFin Rzeczywiście - jak na mój ostatni linia w tekście odpowiedzi :) – Nathan

7

Jak na mój komentarz powyżej ...

Kompilator nie może przekształcać wywnioskować typ T, gdy przeszedł object (który jest skutecznie, co się dzieje)

jednak jest znacznie prostsza opcja - co jest po prostu zrezygnować z użyciem leków generycznych i zmień podpis swojej MyReverse metody public static void MyReverse(IList source) (i gdzie indziej zastąpić List<object> z IList)

tj:

public static void Main(string args[]) 
{ 
    var lists = new List<IList> 
    { 
     new List<Foo>(), 
     new List<Bar>(), 
     new List<Bar>() 
    }; 

    ReverseLists(lists); 
} 

public static void MyReverse(IList source) 
{ 
    var length = source.Count; 
    var hLength = length/2; 
    for (var i = 0; i < hLength; i++) 
    { 
     var temp = source[i]; 
     source[i] = source[length - 1 - i]; 
     source[length - 1 - i] = temp; 
    } 
} 

public static void ReverseLists(List<IList> sourceLists) 
{ 
    foreach (var sourceList in sourceLists) 
    { 
     MyReverse(sourceList); // Error: Type arguments cannot be inferred from usage 
    } 
} 
public class Foo 
{ 
} 
public class Bar 
{ 
} 
+1

Właśnie zacząłem pisać to. Cieszę się, że mnie do tego przekonałeś. Nauczyło mnie to, że "ILIST" to nie tylko przystanek z przed-generycznych dni. – Rawling

+2

Wprowadzi to niepotrzebne boksowanie i rozpakowywanie w przypadku typów wartości. –

2

Aby uzupełnić odpowiedź Srirama Sakthivela, kluczem do problemu jest to, że Typu nie można wywnioskować z tego, co przekazujesz.Warto zauważyć, że List<T> realizuje IList więc kwestia powyżej można przeformułować używając tablicy parametru listę:

void Main() 
{ 
    var fooList = new List<string>(); 
    var barList = new List<string>(); 
    var bazList = new List<string>(); 
    ReverseLists(fooList, barList, bazList); 
} 

public static void ReverseLists(params IList [] sourceLists) 
{ 
    foreach (var sourceList in sourceLists) 
    { 
     MyReverse(sourceList); 
    } 
} 

public static void MyReverse(IList source) 
{ 
    var length = source.Count; 
    var hLength = length/2; 
    for (var i = 0; i < hLength; i++) 
    { 
     var temp = source[i]; 
     source[i] = source[length - 1 - i]; 
     source[length - 1 - i] = temp; 
    } 
} 
0

zmiany tej linii w oryginalnym post:

MyReverse(sourceList); // Error: Type arguments cannot be inferred from usage 

do tego:

MyReverse(((System.Collections.IList)sourceList).Cast<object>().ToList()); 
Powiązane problemy