我想创建一个通用的包装类:
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#中使用它?
答案 0 :(得分: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#调用Wrap
> let wrapped = Wrap<MyIntWrapper,_> 5;;
val wrapped : MyIntWrapper = I'm wrapping an integer with value 5
在我看来,这在F#中并不是非常惯用,我宁愿使用Discriminated Unions和Pattern Matching进行包装/解包,但我不确切知道你的具体情况。
是否可以在C#中使用它?
当然,但是如果你从C#调用Wrap
,你就会回到C#类型推断,并且必须指定第二个类型参数。
答案 1 :(得分:3)
实施自定义隐式转化:
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;
答案 2 :(得分: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 :(得分:3)
您可以使用辅助类来将通用参数分成两个单独的调用,从而可以推断出所需的调用。 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
推断,返回该助手类的实例,允许您将方法调用链接在一起以获取你想要什么。