4

This is related to this question.

I'd like to create a generic wrapper class:

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

With the following extensions:

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;
    }
}

Now assume a concrete Wrapper:

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

I would like to call the Wrap extension like this:

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

This is not possible because in c# we need to provide both type arguments to the Wrap extension. (it's all or nothing)

Apparently partial inference is possible in F#.

How would the above code look in F#?

Would it be possible to use it from C#?

Community
  • 1
  • 1
3dGrabber
  • 4,710
  • 1
  • 34
  • 42
  • 5
    Why don't you just define an implicit conversion? You can than write `MyIntWrapper wrapped = 42;` – Jakub Konecki Dec 27 '12 at 17:30
  • 2
    Why do you have multiple wrapper classes? Just use a single wrapper. Also, as said, implicit conversions are great with wrapper classes. – Servy Dec 27 '12 at 17:31

4 Answers4

4

Apparently partial inference is possible in F#.

Yes, only W would need to be specified in your example. T will be inferred.


How would the above code look in 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

And you can call Wrap from F# interactive this way

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

In my opinion this is not very idiomatic in F#, I would rather use Discriminated Unions and Pattern Matching for wrapping/unwrapping but I don't know exactly what's your specific case.


Would it be possible to use it from C#?

Sure, but if you call Wrap from C# you're back to C# type inference and will have to specify the second type parameter.

Gus
  • 25,839
  • 2
  • 51
  • 76
3

Implement custom implicit conversion:

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;
}

You can then write:

MyIntWrapper wrapped = 42;
Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
Jakub Konecki
  • 45,581
  • 7
  • 87
  • 126
  • Why do you insist on it being generic? – Jakub Konecki Dec 27 '12 at 18:13
  • Thank you, but that is not what I'm looking for. _I'd like to create a_ **generic** _wrapper class_. Your answer is just another kind of syntactic sugar for `MyIntWrapper`, but not for any other class derived from `Wrapper`. You could as well write a non-generic `Wrap` extension for `MyIntWrapper` with almost the same effect. – 3dGrabber Dec 27 '12 at 18:14
  • _Why do you insist on it being generic?_ Avoid writing boiler-plate wrapper code – 3dGrabber Dec 27 '12 at 18:19
  • 1
    @3D-Grabber Why do you have multiple generic wrappers? Why not just one single wrapper class (non-abstract) that can wrap anything? The reason he's defining this behavior multiple times is a direct result of you defining multiple wrapper classes. For the record though, the conversion *to* the wrapper needs to be in the derived class, but the conversion *from* the wrapper can be in the abstract class – Servy Dec 27 '12 at 18:31
  • @Servy: Wrapper aka Adapter Pattern. I need different adapters for different purposes (e.g. different ViewModels in MVVM), hence the need for subclassing. Let me know if you have a better solution for those szenarios – 3dGrabber Dec 27 '12 at 18:40
3

You can do this in 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

And then use it from C# like this:

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

Is that what you want?

Daniel
  • 47,404
  • 11
  • 101
  • 179
3

You can use a helper class to allow you to separate the generic parmeters into two separate calls, allowing you to infer the one you want. LinqPad sample:

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;
    }
}

In particular, notice the new class WrapHelper<T>, which exposes the method To<W>. To that method you pass in an explicit generic argument, but to the original Wrap<T> method, it's inferred by the this T, returning an instance of that helper class, allowing you to chain the method calls together to get what you want.

Kirk Woll
  • 76,112
  • 22
  • 180
  • 195
  • Thanks, I know that fluent interface separation trick :) ... just looking for a more syntactically terse solution – 3dGrabber Dec 27 '12 at 18:23
  • Terse for the caller or the implementer of your static wrapper methods? Not sure why `5.Wrap().To()` is considered wordy? – Kirk Woll Dec 27 '12 at 18:25
  • It's certainly a good solution. Problem with fluent interfaces is that some people (consumers of the API) are not used to it and find it confusing ("Wrap is returning a WrapHelper?, what am I supposed to do with that?"). I tried this sort of solution before. Also, I'm sort of a perfectionist... – 3dGrabber Dec 27 '12 at 18:31
  • @KirkWoll Well, when you compare it to a solution involving implicit conversions that requires *no* code for the caller... – Servy Dec 27 '12 at 18:32
  • @Servy, in an invented (or other) language? I was trying to provide an ideal C# solution. – Kirk Woll Dec 27 '12 at 18:36
  • @KirkWoll C# has user defined implicit conversions...why isn't that part of the ideal solution? – Servy Dec 27 '12 at 18:37
  • @Servy, how do you propose to make that work? If you have a suggestion, by all means propose an answer. – Kirk Woll Dec 27 '12 at 18:47
  • @KirkWoll There is already an answer to this question that does what I've described; in fact it's the most highly upvoted answer to this question (and always has been). – Servy Dec 27 '12 at 18:48
  • 1
    +1 The solution above is not generic and thus requires a class to be written for every type to be used, so it is likely not acceptable since the asker wanted a generic solution. This is the best way to do it and I do not consider Fluent interfaces to be a difficult concept, especially with Intellisense where you can hit the period key and see the methods available to call. Further, you can mark the method returning the helper class with the PureAttribute and Resharper will give you a warning if you don't use the returned helper. – jam40jeff Dec 27 '12 at 21:16