簡體   English   中英

強類型參數

[英]Strongly Typed Type Parameters

出於性能原因,我不能使用Automapper或任何其他基於約定的映射工具。 理想情況下,我想要一個可以調用的方法簽名...例如-mapper.Map <,>()但是以下語法無效

   public MyModelView Map<Tin, Tout>(Tin source)
        where Tin: MyModelDto 
        where Tout: MyModelView 
    {
        return new MyModelView{
             Prop1 = source.Prop1;
        };
    }

    public MyModelDto Map<Tin, Tout>(Tin source)
        where Tin: MyModelView 
        where Tout: MyModelDto 
    {
        return new MyModelDto {
             Prop1 = source.Prop1;
        };
    }

以下是一種可行的解決方案,但我個人覺得很丑。

    public void Map(MyModelDto source, out MyModelView outParam)
    {
        outParam = new MyModelView();
        outParam.Prop1 = source.Prop1;
    }

    public void Map(MyModelView source, out MyModelDto outParam)
    {
        outParam = new MyModelDto ();
        outParam.Prop1 = source.Prop1;
    }

    MyModelDto myDto = new MyModelDto { Prop1 = "Hello World" };
    MyModelView myView;
    mapper.Map(myDto, out myView);

我的問題是,有沒有辦法在不使用反射的情況下強制使用確定類型,或者使用out參數來強制唯一性,或者out param方法是我將要獲得的最干凈的方法?

更新:因此,我制定了一個滿足我需要的解決方案,它利用了RuntimeTypeHandle,雖然它並不完全理想,但它對我有用,並且使用起來最少的麻煩。 如果我的要求僅允許每個對象進行1個類型轉換,那么我將堅持使用一個更簡單的解決方案。 為了完整性...這是我想出的。

歸因於類型處理的想法: 什么更快,打開字符串或其他類型的輸入?

public class Mapper : IMapper
{
    private delegate object TypeHandler(Object node, Type desiredReturn);

    private static readonly Dictionary<RuntimeTypeHandle, TypeHandler> TypeLibrary = CreateTypeHandler();

    private static Dictionary<RuntimeTypeHandle, TypeHandler> CreateTypeHandler()
    {
        var ret = new Dictionary<RuntimeTypeHandle, TypeHandler>();

        ret[typeof(MyModelDto).TypeHandle] = HandleMyModelDto;
        //ret[typeof (Jill).TypeHandle] = HandleJill;
        //ret[typeof (Marko).TypeHandle] = HandleMarko;
        return ret;
    }

    private static object HandleMyModelDto(object source, Type desiredMapping)
    {
        MyModelDto sourceObj = source as MyModelDto;

        if (desiredMapping == typeof(MyModelView))
        {
            return new MyModelView { Prop1 = sourceObj.Prop1 };
        }
        else if (desiredMapping == typeof (MyModelBusiness))
        {
            return new MyModelBusiness { Prop1 = sourceObj.Prop1 };
        }
    }

    public Tout Map<Tin, Tout>(Tin source)
    {
        TypeHandler handler;
        if (TypeLibrary.TryGetValue(Type.GetTypeHandle(source), out handler))
        {
            return (Tout)handler(source, typeof(Tout));
        }
        else
        {
            //Unexpected type...
            throw new NotImplementedException("Type mapping not implemented");
        }
    }
}

消費如下

Mapper mapper = new Mapper();
MyModelDto myDto = new MyModelDto { Prop1 = "Hello World" };
MyModelView returnVal = mapper.Map<MyModelDto, MyModelView>(myDto);

不幸的是,在C#中,您不能定義具有相同簽名(即相同的方法名稱+參數)的兩個方法來返回兩種不同的類型,但是,例如,如果您的視圖和dto對象實現了一個公共接口,則類似於以下內容:

public interface IMyModel
{
    string Prop1 { get; set; }
}

然后,您可以按以下方式定義Map方法:

public Tout Map<Tin, Tout>(Tin source)
    where Tin: IMyModel 
    where Tout: IMyModel, new() 
{
    var result = new Tout();
    result.Prop1 = source.Prop1;
    return result;
}

然后可以將其稱為:

var view = Map<MyModelDto, MyModelView>(dtoInstance);
var dto = Map<MyModelView, MyModelDto>(viewInstance);

如果您知道涉及的類型和成員,為什么要嘗試使Map通用? 忘記“ Tin和“ Tout ,只使用您接受/返回的基本類型。

public MyModelView Map(MyModelDto source)
{
    return new MyModelView{
         Prop1 = source.Prop1
    };
}

基於MyModelDto任何內容MyModelDto必須具有Prop1並且基於MyModelView任何內容MyModelView必須具有Prop1 即使它是任一子類的子類,也要在不進行反思的情況下使用它,則必須在基類中定義這些成員。 如果每個子類都有變體,例如額外的成員,則需要為每個合法的轉換創建映射器函數的自定義版本(實際上,不要使用類繼承,因為這樣做只會增加復雜性,在此沒有任何好處)。 這將是最快的代碼,但要以維護映射例程為代價。

僅當您僅需要將MyModelDto映射到另一種對象類型時,這才起作用。 假設我要將dto保存到我的數據庫對象層或其他視圖模型中。 我將無法創建唯一的方法簽名,因為C#不會將返回類型視為唯一的方法簽名的一部分

那么你也能:

public void Map(MyModelDto source, out MyModelView target)
{
    target = new MyModelView {
         Prop1 = source.Prop1
    };
}

但我寧願建議你不要這樣做,因為有工作out PARAMS可以是惱人。 相反,請使用MapToViewMapToModel等或保留Map但要通過不同的命名空間(對於擴展方法,可能是最佳選擇)或靜態類名(執行MapView.Convert()MapModel.Convert() MapView.Convert() MapModel.Convert() 命名空間可能是最好的策略,因為在我看來,在大多數代碼部分中,您將只處理一個版本或另一個版本,而不是兩個版本。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM