2017-02-22 7 views
1

나는 모든 속성 Id와 Legacy_id를 가지고 있다고 알고있는 여러 유형의 객체를 비교해야합니다. 불행히도 유형이 데이터베이스 스키마에서 왔기 때문에 인터페이스를 추가 할 수 없습니다. 다음과 같은 비교기가 작동하기를 기대했습니다.유형 유추가 입력 내용이 유형에 존재하는지 자동으로 결정하지 않는 이유는 무엇입니까?

type Comparer<'T >()= 
    interface System.Collections.Generic.IEqualityComparer<'T> with 
    member this.Equals (o1:'T,o2:'T)= 
     o1.Legacy_id=o2.Legacy_id 
    member this.GetHashCode(o:'T)= 
     o.Id+o.Legacy_id 

또한 유형이있는 비교 자 유형의 인스턴스가 있습니다. 이론적으로 컴파일러는 충분한 정보를 가지고 있습니다.

그러나 "이 프로그램 지점 이전의 정보를 기준으로 불확정 유형의 개체를 찾습니다. 개체 유형을 제한하기 위해이 프로그램 지점 전에 형식 주석이 필요할 수 있습니다. 이로 인해 조회가 허용 될 수 있습니다. 해결할 것 "이라고 말했다.

여기 F #이 실패하는 이유가 궁금합니다. 실제/이론적 제약이 있습니까 아니면 구현되지 않았습니까? 그런 추론은 매우 유용 할 수 있습니다.

나는 F # 컴파일러에 대한 설명이 앞으로 걷는 것 같다고 생각합니다. C#에는 제한이 없습니다. 그것은 오류 메시지가 불평하는 것입니다. 그래?

+2

주어진''T''가'Id'라는 속성을 가질 수 없기 때문에 이것은 컴파일 될 수 없습니다. 이 클래스를'Comparer '으로 인스턴스화하면 어떻게 될까요? –

+0

@FyodorSoikin하지만 Compareer 으로 인스턴스화하지 않습니다. 그래서 올바른 유형으로 인스턴스화 할 때까지 컴파일 작업이 가능합니다. – alehro

+0

그래도 상상해보십시오. int를 시도하면 어떻게 될 것이라고 생각합니까? –

답변

6

멤버 제약 조건을 형식에 사용할 수 없으므로이를 수행 할 수 없습니다. here을 참조하십시오.

당신이 할 수있는 일은 특정 유형에 대한 명시 적 동일성 검사 및 해시 코드 생성 기능을 허용하는 비교 자 클래스를 만드는 것입니다.

let inline id obj = 
    (^T : (member Id : int) (obj)) 

let inline legacyId obj = 
    (^T : (member Legacy_id : int) (obj)) 

let inline equals o1 o2 = 
    id o1 = id o2 

let inline hash o = 
    id o + legacyId o 

let inline createComparer< ^T when ^T : (member Id: int) and ^T : (member Legacy_id : int) >() = 
    Comparer<^T>(equals, hash) :> System.Collections.Generic.IEqualityComparer<^T> 

당신이 두 개의 필수 속성이 어떤 종류의 TestType을 말해봐 :

type Comparer<'T>(equalityFunc, hashFunc) = 
    interface System.Collections.Generic.IEqualityComparer<'T> with 
    member this.Equals (o1:'T,o2:'T)= 
     equalityFunc o1 o2 
    member this.GetHashCode(o:'T)= 
     hashFunc o 

그런 다음 당신은 당신이 부과하고자하는 제약 조건과 일치 유형에 대해 위의 인스턴스를 생성하는 인라인 기능을 사용할 수 있습니다 :

type TestType = 
    member this.Legacy_id = 7 
    member this.Id = 9 

당신이 할 다음, 예를 들어, createComparer<TestType>()이 유형에 적합한 같음 비교자를 생성합니다.

+0

따라서 C#의 제네릭 형식은 정적으로 컴파일되지 않습니다. C++과 같은 유형의 조작이 가능하지 않은 이유는 언제나 궁금합니다. 이제 내가 이해하는 것 같다. 따라서 F # 's^T를 가지고 F # J에서 Aleksandrescu의 템플릿 메타 프로그래밍을 재 구현할 수 있습니다. – alehro

+3

@alehro F #의 정적으로 해석 된 유형 매개 변수는 C++ 템플릿보다 훨씬 유연성이 떨어집니다. 예를 들어 매개 변수 값을 매개 변수화 할 수있는 방법은 없습니다. 'template '그러나 그들은 더 높은 종류의 다형성을 시뮬레이션하는 것과 같은 것을 허용합니다. – TheInnerLight

5

간결한 방법으로 비교자를 얻습니다. 만드는 HashSet은 다음과 같습니다

let inline getId o = (^T : (member Id : int) o) 
let inline getLegacyId o = (^T : (member Legacy_id : int) o) 

let inline legacyComparer< ^T when ^T : (member Id : int) and ^T : (member Legacy_id : int)>() = 
    { new System.Collections.Generic.IEqualityComparer<'T> with 
      member __.GetHashCode o = getId o + getLegacyId o 
      member __.Equals(o1, o2) = getLegacyId o1 = getLegacyId o2 } 

형식 유추는 모든 인스턴스화에 대한 몇 가지 제네릭 형식 매개 변수를 만족하는 임의의 조건 (공칭 대 구조 타입 시스템 참조) 것을 증명하지 유형을 추론에 관한 것입니다. 이미 정적으로 해결 된 타입 파라미터에 대해서는 많은 좋은 해답이 있습니다.