2012-12-27 32 views
4

這與this question有關。如何編寫我的包裝類以使用部分泛型類型推斷?

我想創建一個通用的包裝類:

public abstract class Wrapper<T> 
{ 
    internal protected T Wrapped { get; set; } 
} 

具有以下擴展名:

public static class WrapperExtensions 
{ 
    public static W Wrap<W,T>(this T wrapped) where W:Wrapper<T>,new() 
    { 
     return new W {Wrapped = wrapped}; 
    } 

    public static T Unwrap<T>(this Wrapper<T> w) 
    { 
     return w.Wrapped; 
    } 
} 

現在假設一個具體的包裝:

public class MyIntWrapper : Wrapper<int> 
{ 
    public override string ToString() 
    { 
     return "I am wrapping an integer with value " + Wrapped; 
    } 
} 

我想撥打這樣的Wrap擴展名:

MyIntWrapper wrapped = 42.Wrap<MyIntWrapper>(); 

這是不可能的,因爲在c#中我們需要爲Wrap擴展提供兩個類型參數。 (這是全有或全無)

顯然部分推斷有可能在F#。

上面的代碼在F#中的外觀如何?

是否有可能在C#中使用它?

+5

你爲什麼不直接定義一個隱式轉換?你可以寫'MyIntWrapper wrapped = 42;' –

+2

爲什麼你有多個包裝類?只需使用一個包裝。另外,如前所述,隱式轉換對於包裝類非常有用。 – Servy

回答

4

顯然部分推斷有可能在F#。

是的,只有W¯¯需要在你的例子進行說明。 T將被推斷。


如何將上面的代碼看在F#?

[<AbstractClass>] 
type Wrapper<'a>() = 
    [<DefaultValue>] 
    val mutable internal Wrapped : 'a 

let Wrap<'W,'T when 'W :> Wrapper<'T> and 'W: (new: unit -> 'W)> (wrapped: 'T): 'W = 
    let instance = new 'W() 
    instance.Wrapped <- wrapped 
    instance 

let Unwrap (w: Wrapper<_>) = w.Wrapped 

type MyIntWrapper() = 
    inherit Wrapper<int>() 
    override this.ToString() = 
     sprintf "I'm wrapping an integer with value %d" this.Wrapped 

而且你可以從F#互動這種方式叫裹

> let wrapped = Wrap<MyIntWrapper,_> 5;; 
val wrapped : MyIntWrapper = I'm wrapping an integer with value 5 

在我看來,這不是在F#很地道的,我寧願用識別聯合和模式匹配的包裝/展開,但我不確切知道你的具體情況。


有沒有可能從C#中使用它?

當然可以,但如果你從C#調用Wrap你回C#類型推斷,將有以指定第二個類型參數。

+0

謝謝,現場回答這個問題,不幸的是不是我所希望的結果。 – 3dGrabber

3

實現自定義的隱式轉換:

MSDN sample

struct MyIntWrapper 
{ 
    public MyIntWrapper(int value) 
    { 
     this.value = value; 
    } 

    static public implicit operator MyIntWrapper(int value) 
    { 
     return new MyIntWrapper(value); 
    } 

    static public explicit operator int(MyIntWrapper wrapper) 
    { 
     return wrapper.value; 
    } 

    private int value; 
} 

然後,您可以寫:

MyIntWrapper wrapped = 42; 
+0

爲什麼你堅持要通用? –

+0

謝謝,但那不是我要找的。 _我想創建a_ **通用** _wrapper class_。 你的回答會是另一種對'MyIntWrapper',而不是從'包裝派生的任何其他類'語法糖。 你也可以爲MyIntWrapper寫一個非泛型的'Wrap'擴展,效果幾乎相同。 – 3dGrabber

+0

_why你堅持它是通用的?_ 避免編寫鍋爐板包裝代碼 – 3dGrabber

3

您可以在F#中做到這一點:

open System.Runtime.CompilerServices 

type Wrapper<'T> = 
    abstract Wrapped : 'T 

[<Extension>] 
module WrapperExtensions = 

    [<Extension>] 
    let Wrap wrapped = { new Wrapper<_> with member x.Wrapped = wrapped } 

    [<Extension>] 
    let Unwrap (w: Wrapper<_>) = w.Wrapped 

,然後用它從C#這樣的:

var wrapped = 42.Wrap(); 
wrapped.Unwrap(); 

這是你想要的嗎?

3

您可以使用一個輔助類,讓您通用parmeters分成兩個單獨的呼叫,讓你推斷你想要的。 LinqPad樣品:

void Main() 
{ 
    MyIntWrapper wrapped = 42.Wrap().To<MyIntWrapper>(); 
} 

public abstract class Wrapper<T> 
{ 
    internal protected T Wrapped { get; set; } 
} 

public static class WrapperExtensions 
{ 
    public static WrapHelper<T> Wrap<T>(this T wrapped) 
    { 
     return new WrapHelper<T>(wrapped); 
    } 

    public static T Unwrap<T>(this Wrapper<T> w) 
    { 
     return w.Wrapped; 
    } 

    public class WrapHelper<T> 
    { 
     private T wrapped; 

     public WrapHelper(T wrapped) 
     { 
      this.wrapped = wrapped; 
     } 

     public W To<W>() where W : Wrapper<T>, new() 
     { 
      return new W {Wrapped = wrapped}; 
     } 
    } 
} 

public class MyIntWrapper : Wrapper<int> 
{ 
    public override string ToString() 
    { 
     return "I am wrapping an integer with value " + Wrapped; 
    } 
} 

特別地,發現新的類WrapHelper<T>,其暴露所述方法To<W>。對於該方法,您傳遞一個明確的泛型參數,但是傳遞給原始的Wrap<T>方法,它由this T推斷,返回該輔助類的一個實例,允許您將方法調用鏈接在一起以獲取所需內容。

+0

謝謝,我知道,流暢的界面分離招:) ...只是尋找一個更簡潔的語法解決 – 3dGrabber

+0

簡潔呼叫者或您的靜態包裝方法的實施者?不知道爲什麼'5.Wrap()。至()'被認爲是羅嗦嗎? –

+0

這當然是一個很好的解決方案。流暢接口的問題是有些人(API的消費者)不習慣它,並發現它令人困惑(「Wrap 正在返回一個WrapHelper ?,我應該怎麼做?」)。我以前嘗試過這種解決方案。此外,我是一個完美主義者...... – 3dGrabber