변환

2010-07-09 9 views
4

나는 (이 예제) 다음과 같은 클래스와 확장 클래스는 한 :변환

public class Person<T> 
{ 
    public T Value { get; set; } 
} 

public static class PersonExt 
{ 
    public static void Process<TResult>(this Person<IEnumerable<TResult>> p) 
    { 
     // Do something with .Any(). 
     Console.WriteLine(p.Value.Any()); 
    } 
} 
나는 다음과 일하는 것이 뭔가를 쓸 수 기다리고 있었다

하지만, 아무튼 't :

var x = new Person<List<String>>(); 
x.Process(); 

List는 상속 트리에서 IEnumerable보다 하위이므로 유효하지 않아야합니까? 그것은 제가 직접 Person<IEnumerable<String>>을 작성하면 작동합니다. 왜냐하면 그것은 직접적인 유형이기 때문입니다.

나는 .Any() 메서드를 사용해야하기 때문에 T가 IEnumerable<Something>을 구현하는 한 Person<T>의 모든 메서드에 적용 할 수있는 확장 메서드를 사용하려고합니다.

편집 : 아마도 공분산에 대한 이해가 꺼져 있을까요? IEnumerable<String>IEnumerable<Object>으로 변환해야하지만 IList<String>IEnumerable<String>으로 변환 할 수 없습니다.

EDIT2 : .net 4.0을 사용하여 I 입니다.

+0

, 사람들은 다른 개념은,'IList의 는'변환이이 허용된다 그 이유는,''IEnumerable을 '를 구현 ICollection에 을'구현이 점에서 공변 또는 contravariant 수하는 것이 필요는 없습니다 케이스. –

+0

지난 몇 분 동안 약간의 독서를 한 후에 공동/반항에 대해 이해하고 있습니다. 그렇다면 내가 왜 쓴 것이 유효하지 않은가? 그것은 직관적 인 것 같습니다 ... – TheCloudlessSky

+1

C# 4.0에서이 작업을 수행했다면 작동 할 것이라고 추측했지만 C# 3.0 이하에서는 작동하지 않습니다. –

답변

2
내가 IEnumerable<String>IEnumerable<Object>로 변환해야합니다 알고

하지만 는 IList<String>IEnumerable<String>로 변환 할 수 없습니다?

IList<String>IEnumerable<String>으로 변환 할 수 있습니다. 문제는 Person<List<String>>Person<IEnumerable<String>>으로 변환하려고하는데 불법입니다. 예를 들어, 쓰기 완벽하게 유효한입니다 :

var x = new Person<IEnumerable<String>>(); 
x.Value = new string[0]; 

을 값 유형 IEnumerable<String>이며 문자열 배열이 IEnumerable<String> 때문이다. 그러나 다음과 같이 쓸 수 없습니다.

var x = new Person<List<String>>(); 
x.Value = new string[0]; 

값은 List<String>이므로 값을 쓸 수 없습니다. Person<IEnumerable<String>>을 사용할 수있는 모든 곳에서 Person<List<String>>을 사용할 수 없으므로 법적 근거가 아닙니다. 당신이 당신의 확장 메서드에 두 번째 유형 매개 변수를 추가하면 당신은 당신이 원하는 비슷한 뭔가를 할 수

참고 :

public static void Process<TResult, TList>(this Person<TList> p) 
    where TList : IEnumerable<TResult> 
{ 
    Console.WriteLine(p.Value.Any()); 
} 

불행하게도, 컴파일러는 두 유형의 매개 변수를 추정 할 수 없기 때문에

var x = new Person<List<String>>(); 
x.Process<String, List<String>>(); 

C# 4를 사용하는 경우 다음과 같이 호출해야합니다.

public interface IPerson<out T> 
{ 
    T Value { get; } 
} 

public class Person<T> 
    : IPerson<T> 
{ 
    public T Value { get; set; } 
} 

을 그리고 다음과 같이 확장 방법을 쓰기 : 0과 공분산을 사용할 수 있습니다, 당신은 사람에 대한 공변 인터페이스를 정의 할 수 있습니다 IPerson<T>.Value 이후

public static void Process<TResult>(this IPerson<IEnumerable<TResult>> p) 
{ 
    // Do something with .Any(). 
    Console.WriteLine(p.Value.Any()); 
} 

가 읽기 전용하는 IPerson<List<String>>을 수행 할 수 있습니다 IPerson<IEnumerable<String>>이 될 수있는 모든 곳에서 사용할 수 있으며 변환이 유효합니다.

+0

예, 인터페이스에서'out' 키워드가 누락되었습니다. 감사! – TheCloudlessSky

0

공분산은 언급했지만 실제로 사용하지는 마십시오. 일반 매개 변수에 in 또는 out을 지정해야합니다. co/contravariance는 클래스 타입에서는 작동하지 않는다. 인터페이스에 적용되어야합니다.

따라서, 인터페이스를 도입하고 공변 만들기 :

public interface IPerson<out T> 
{ 
    T Value { get; } 
} 

public class Person<T> : IPerson<T> 
{ 
    public T Value { get; set; } 
} 

public static class PersonExt 
{ 
    public static void Process<TResult>(this IPerson<IEnumerable<TResult>> p) 
    { 
    // Do something with .Any(). 
    Console.WriteLine(p.Value.Any()); 
    } 
} 

이 코드를 컴파일 할 수 있습니다 :

var x = new Person<List<String>>(); 
x.Process(); 
1

난 당신이 아주 제네릭의 올바른 사용을 파악했는지 모르겠어요. 어떤 경우에도 ...

유일한 잘못된 것은 사용자의 선언 확장 방법과 확장 방법을 제한하는 방법입니다.

public static class ThingExtensions 
{ 
    public static void Process<T>(this Thing<T> p) 
     where T : IEnumerable<string> 
    { 
     // Do something with .Any(). 
     Console.WriteLine(p.Value.Any()); 
    } 
} 

정말했던 모든 우리가 Person<List<string>> 정말 무엇을 끊었 지하지 않을 정도로 PersonThing에 이름을 바꿀 수 있습니다.

public class Thing<T> 
{ 
    public T Value { get; set; } 
} 

class ListOfString : List<string> 
{ } 

class Program 
{ 
    static void Main(string[] args) 
    { 
     var x = new Thing<ListOfString>(); 
     x.Value = new ListOfString(); 
     x.Process(); 

     x.Value.Add("asd"); 
     x.Process(); 


     var x2 = new Thing<int>(); 
     // Error 1 The type 'int' cannot be used as type parameter 'T' 
     // in the generic type or method 
     // 'ThingExtensions.Process<T>(Thing<T>)'. 
     // There is no boxing conversion from 'int' to 
     // 'System.Collections.Generic.IEnumerable<string>'.  
     //x2.Process(); 

     Console.Read(); 
    } 
} 

제네릭 제약 조건을 Thing<T>으로 옮길 수도 있습니다. 당신의 편집에 관하여

+0

이것은 .net 3.5 (VS 2008) –

+0

이있는 콘솔 응용 프로그램이라고 언급 했어야합니다. ListOfString을 List 또는 그 밖에 IEnumerable 을 구현하는 것으로 대체 할 수도 있습니다. –