2014-10-21 25 views
9

için tatmin etmediği Func için, sahneyi ayarlamama izin ver: Kodumuzda, bir işlev alan ve bazı oturum açma işlemi gerçekleştiren ve sonuçta sonuç veren bir işlevimiz var. . Böyle küçük bir şey görünüyor. Bununla kullanımda A, A, T, A, T

TResponse LoggedApiCall<TResponse>(Func<BaseRequest, BaseResponse> apiCall, ...) 
    where TResponse : BaseResponse; 

Ben

namespace Name.Space.Base { 
    public class BaseRequest { 
     ... 
    } 
} 

namespace Name.Space.Base { 
    public class BaseResponse { 
     ... 
    } 
} 

namespace Some.Other.Name.Space { 
    public class Request : BaseRequest { 
     ... 
    } 
} 

namespace Name.Space { 
    public class Response<TPayload> : BaseResponse { 
     ... 
    } 
} 

Yani dört aşağıdaki nesneleri bu bazı Birim Testleri desteklemek amacıyla sahte LoggedApiCall (Adedi kullanarak) çalışıyorum ile. Temel tip kısıtlamalarını karşılayan bir işlevi ve Mock üzerinde .Setup() gerçekleştirmek için ortak bir yöntem oluşturmak için eşleşmeleri de yazılan bir yanıtı iletmemize izin veren genel bir yöntem yazıyorum.

Bu şuna benzer:

protected IReturnsResult<IService> SetupLoggedApiCall<TRequest, TResponse>(
    Func<TRequest, TResponse> function, 
    TResponse response 
    ) 
    where TRequest : BaseRequest 
    where TResponse : BaseResponse 
{ 
    var baseFunction = function as Func<BaseRequest, BaseResponse>; 
    return _mockService.Setup(service => service.LoggedApiCall<TResponse>(
      baseFunction, /*other parameters * 
     )) 
     .Returns(response); 
    } 
} 

Ben işlevini döküm çalışılıyor sebebi yoksa, ben intellisense hatayı

Argument type 'System.Func<TRequest, TResponse>' is not assignable to 
parameter type 'System.Func<Name.Space.Base.BaseRequest, Name.Space.Base.BaseResponse>' 

Bu almak, biraz bulmak TRUE ve TRESIGNE olarak bemusing, sırasıyla BaseRequest ve BaseResponse tarafından sınırlandırılır, fakat eğer etrafımdaki bir iş varsa, yapmalıyım. döküm

var baseFunction = function as Func<BaseRequest, BaseResponse> 

gerçekleştirirken Ancak, boş olarak çözer. Bu, SetupLoggedApiCall'a iletilen parametre üzerindeki yukarıda belirtilen kısıtlamalar nedeniyle şaşkınlık buluyorum. Ben biraz daha kod hata ayıklama iken kazma yaptım ve aşağıdaki var: Bu görüldüğü gibi

function is Func<TRequest, TResponse>  | true 
function is Func<TRequest, BaseResponse> | true 
function is Func<BaseRequest, BaseResponse> | false 

, TResponse BaseResponse tatmin etmek ve hiçbir sorunları ile buna dökülebilir devam ediyor. Ancak, TRequest'ten BaseRequest'e geçmeye çalıştığımız anda başarısız olur. Sadece herhangi bir yanlış türlerini ithal nerede bir durumun içine emin ben elde değildi yapmak ya da bir şey ben bu bizi takip etmek:

typeof(TRequest).BaseType == typeof(BaseRequest) | true 

Yani, herkes bana söyleyebilir: Verilen o TRequest bir BaseRequest bu varlık kararına işaret cast TRequest konusunda başarısız?

Yeni bir kod projesinde bu sorunun çözülmesine ve sorunun çözülmesine başlayacağız (gerçekte, kodumuz aşağıda olduğu kadar basit değildir, çekirdek için basitleştirilmiştir) ve hangi noktada olduğunu görelim başarısız olur ve bir şey bulursak güncelleniriz, ancak herhangi bir içgörü takdir edilecektir. Bunun anlamı içinde, SetupLoggedApiCall yapılan en az derleme zamanında, mutlu ne ediliyordu

TResponse LoggedApiCall<TRequest, TResponse>(Func<TRequest, TResponse> apiCall, ...) 
    where TRequest : BaseRequest where TResponse : BaseResponse 

okumak için ben LoggedApiCall tanımını güncellenmiş @EugenePodskal öneri aşağıdaki üzerine

Güncelleme 1

Geçti geçerli ancak, alay hizmetine yapılan çağrı hala null değerini döndürür. Bu çağrı için interceptor geri vekil nesnesine ve aşağı kazdık ve bu keşfetti:

bir yazım hatası değil
IService service => service.LoggedApiCall<Request, Response>(, /*other params*/) 

. İlk parametre sadece önleyici eksik.Bence bu soru, Func'den daha çok Mock ile ilgili olmaktan başka bir şey değildir, ama bu önleyici, o paramaterin basitçe kaybolmasına neden olabilecek şeylere ışık tutabilecek bir lamba ifadesi olduğunu görüyor mu?

+1

Bunun nasıl bir kopya olduğunu göremiyorum - bağlantılı soru 'Func'nin varyansı ile ilgili bazı detayların açıklığa kavuşturulmasıdır, bu soru aslında genel olarak bir varyansın açıklamasını/açıklamasını gerektirir. Bağlam aynı, ama soru değil. Diğer sorunun cevabı muhtemelen bu kişinin kurbanına yardımcı olmayacaktır. –

+0

@AntP Bağlantılı soru "neden eylem " için "" eylemi atamıyorum ve bu soru aslında "Func " işlevini "Func " türüne çeviremiyorum. – Rawling

+1

@Rawling Elbette, şeyleri aşırı basitleştirmek isterseniz. Diğer soru ve cevapları zaten varyans kavramını kavradığını varsayar, bu yüzden bu soruya verilen cevaplar buna uygulandığında yararsızdır. Örneğin. "Func " işlevini 'Func ''e neden çeviremiyorum? 'Ile doğru yanıt verilemez." Kovaryansınız ve yanlış yoldan yanlış yol aldınız. " –

cevap

1

bakmak gerekir konu Covariance and Contravariance in Generics

http://msdn.microsoft.com/en-us/library/dd799517(v=vs.110).aspx

yani ".NET Framework 4, böyle Func olarak Func jenerik delegeler, kovaryant dönüş türleri ve kontravaryant parametre türleri vardır." Dir

Bunu düşünmek eğer, hem de mantıklı

..

Func<Apple, AppleProduct> MakeAppleProduct = new Func....; 

// Assume the cast is allowed at runtime and doesn't throw. 
Func<Fruit, FruitProduct> MakeFruitProduct = (Func<Fruit, FruitProduct>) MakeAppleProduct; 

//Returns an instance of AppleProduct 
MakeFruitProduct(appleInstance); 

//Orange is also a Fruit, and hence we are allowed to pass it? Should it be allowed? 
MakeFruitProduct(orangeInstance); 

Dolayısıyla fonksiyon parametreleri için, baz tipine döküm kadar izin vermek istemiyoruz. fonksiyonu ilk olarak AppleProduct örneği dönmek için beyan edilmiştir, dönüş, değerleri Diğer taraftan

,% 100 (tip) o FuitProduct bir örneğini verir demek güvenlidir (AppleProduct için temel sınıf)

İlgili konular