2013-07-19 2 views
5

아무렇지도 않게 내 주변을 두뇌에 괴롭 히면 누구든지 도움이 될 수 있는지 궁금하십니까?ViewModelBuilder 제네릭 캐스팅 문제

정말로 실망스러운 주조 문제를 얻는 것은 확실한 답을 얻을 수 있지만 제네릭 형식 유추 나 다른 것에 대한 제한된 이해로 인해 발생하고있을 것입니다. 사전에

감사합니다!

시나리오는 마법사 사이트의 "단계별"ViewModels입니다. 나는 각각에 대한 빌더 클래스를 만들고, IStepViewModel 콜렉션 인 저에게 다시 게시 된 단계를 위해 특정 빌더를 잡기 위해 팩토리를 사용합니다.

public interface IStepViewModelBuilderFactory 
{ 
    IStepModelBuilder<T> Create<T>(T stepViewModel) where T : IStepViewModel; 
    void Release<T>(IStepModelBuilder<T> stepViewModelBuilder) where T : IStepViewModel; 
} 

public interface IStepViewModel 
{ 
} 

public interface IStepModelBuilder<TStepViewModel> : IModelBuilder<TStepViewModel> where TStepViewModel : IStepViewModel 
{ 
} 

public class SpecificViewModelBuilder : IStepModelBuilder<SpecificStepViewModel> 
{ 
} 

public class SpecificStepViewModel: StepViewModel 
{ 
} 

public abstract class StepViewModel : IStepViewModel 
{ 
} 

실패한 테스트!

[Test] 
public void TestResolution() 
{ 
    var factory = this.container.Resolve<IStepViewModelBuilderFactory>(); 

    IStepViewModel viewModel = new SpecificStepViewModel(); 

    var builder = factory.Create(viewModel); // Here 

    Assert.That(builder, Is.Not.Null); 
} 

문제가 발생했습니다. 를 입력 유형 'Company.Namespace.SpecificViewModelBuilder'의 개체를 캐스팅 할 수

수 없습니다 'Company.Namespace.Builders.IStepModelBuilder`1 [Company.Namespace.IStepViewModel].

container.AddFacility<TypedFactoryFacility>(); 

    .... 

    container 
     .Register(
      Classes 
       .FromAssemblyContaining<StepViewModelSelector>() 
       .BasedOn<StepViewModelSelector>()); 

    container 
     .Register(
      Component 
       .For<IStepViewModelBuilderFactory>() 
       .AsFactory(c => c.SelectedWith<StepViewModelSelector>())); 

스택 트레이스 : Castle.Windsor

이러한
public class StepViewModelSelector : DefaultTypedFactoryComponentSelector 
{   
    protected override Type GetComponentType(System.Reflection.MethodInfo method, object[] arguments) 
    { 
     var arg = arguments[0].GetType(); 
     var specType = typeof(IModelBuilder<>).MakeGenericType(arg); 
     return specType; 
    } 
} 

등록하여 다음

공장 IMPL로

System.InvalidCastException 사용자 코드에 의해 처리되지 않은 하였다
HResult = -21 47467262 Message = 'Company.Namespace.SpecificViewModelBuilder'유형의 객체를 캐스팅하여 'Company.Namespace.IStepModelBuilder`1 [Company.Namespace.IStepViewModel]'을 입력 할 수 없습니다. 자료 = DynamicProxyGenAssembly2 스택 트레이스 \ 프로젝트 \ 인프라 \의 ViewModelBuilderFactoryTests.cs : 라인 61
C에서 Tests.Infrastructure.ViewModelBuilderFactoryTests.TestResolution()에서 Castle.Proxies.IStepViewModelBuilderFactoryProxy.Create [T (T stepViewModel) 에서 의 InnerException :

편집 : IModelBuilder<T> 인터페이스

IModelBuilder<T> 당신이 여기에 표시되지 않는 하나의 인터페이스가 있습니다
public interface IModelBuilder<TViewModel> 
{ 
    TViewModel Build(); 
    TViewModel Rebuild(TViewModel model); 
} 
+0

해당 팩토리의 Create 메소드 구현 중에 오류가 발생하는 것처럼 보입니다. 제발 그걸 제공해 줄 수 있니? – br1

+0

저는 Castle.Windsor의 typed factory impl을 사용하고 있습니다. 사용하는 필터 선택기에 추가하십시오. – M05Pr1mty

+0

괜찮 으면 예외의 .ToString() 덤프를 추가하십시오. 스택 트레이스를보고 싶습니다. – br1

답변

4

인터페이스를 사용하지만 문제를 해결하기위한 핵심 인터페이스입니다. 당신이 이후로 볼 수 있습니다 일반적인 공분산을 사용하는 경우 내가 있으리라 믿고있어

는 현재이

public interface IModelBuilder<T> { } 

같이 정의합니다.4 NET,이처럼 인터페이스를 정의하여 문제를 해결 할 수 있습니다 :

public interface IModelBuilder<out T> { } 

out 수정은 IStepModelBuilder<SpecificStepViewModel>에서 IStepModelBuilder<IStepViewModel>으로 캐스팅 할 수 귀하의 인터페이스 공변을합니다. 이것도 인터페이스에 제약을 두어, T을 매개 변수로하지만 반환 값으로 만 메소드를 정의 할 수는 없다는 점에 유의해야합니다.

Covariance and Contravariance here에 대한 자세한 내용을 볼 수 있습니다.

public interface IModelBuilder<T> 
{ 
    T Create(T myViewModel); 
} 

당신이 전달하는 대신 Create에 매개 변수로 T를 전달하는, 그것은 OK의 경우

편집

당신이 당신의 코멘트에서 언급 한 바와 같이, 사용자 인터페이스는 아마 다음과 같이 보입니다 IStepViewModel 또는 T이 아닌 다른 값을 입력하면 문제가 해결됩니다.

public interface IModelBuilder<out T> 
{ 
    T Create(IStepViewModel myViewModel); 
} 

그렇지 않은 경우 시도한 캐스트가 허용되지 않아야합니다.

+0

이 또한 보았습니다, 인터페이스를 추가 할 것입니다,하지만 나는이 매개 변수를 입력 매개 변수로 사용하여 공분산을 사용할 수 있다고 생각하지 않습니다. 따라서 invariant가 필요하다는 것을 알고 있습니다. – M05Pr1mty

+0

감사합니다. 정말 유용합니다.그렇다면이 모든 일을 성취하기 위해이 모든 것을 재구성 할 방법이 있습니까? – M05Pr1mty

+0

@ M05Pr1mty 내 업데이트 된 답변보기 –

2

나는 그 IStepModelBuilder<T>이며, 다음과 같은 두 가지 정의가

public interface IStepViewModelBuilderFactory 
{ 
    IStepModelBuilder<T> Create<T>(T stepViewModel) where T : IStepViewModel; 
    //... rest of the class definition 
} 

public class SpecificViewModelBuilder : IStepModelBuilder<SpecificStepViewModel> 
{ 
} 

(가) 실행을 만들기

, 그것은 그의 반환 값을 생성 유형 (즉 SpecificViewModelBuilder입니다) 캐스트있는 Compatibile하지 생각합니다.

이 할 수없는

, 수동으로이 일을하려고하여 테스트 할 수

class MyTest<T> where T : IStepViewModel 
    { 
     void Test() 
     { 
      IStepModelBuilder<T> cannotImplicitlyCast = new SpecificStepViewModelBuilder(); 
     } 
    } 

편집 :이 수행 할 수 있습니다

일부 (아마 너무 좋지 않다) 아이디어 :

public class ViewModelBuilder<T> : IStepModelBuilder<T> where T : IStepViewModel 
{ 
} 

class MyTest<T> where T : IStepViewModel 
{ 
    void Test() 
    { 
     IStepModelBuilder<T> ok= new ViewModelBuilder<T>(); 
     IStepModelBuilder<SpecificStepViewModel> alsoOk = new ViewModelBuilder<SpecificStepViewModel>(); 
    } 
} 

그래서 당신은 speciali는 수 각 SpecificStepViewModel에 대해 하나의 팩토리를 생성하십시오.

+0

동의합니다.이 문제를 해결하는 방법에 대한 제안 사항이 있습니까? 나는 이것을 너무 오랫동안 보았거나 나를 넘어 섰다. – M05Pr1mty

+0

흥미로운 아이디어 - 나는 나중에 이것을 다시 볼 수 있습니다. 아마도 일종의 추상적 인 factoy이 올바른 특정 공장을 제공하기 위해 포장 될 수 ... 상관없이 고마워! – M05Pr1mty