Phillip Trelford's Array

POKE 36879,255

Disinherited Types

One of the things that’s always bugged me when using Windows UI libraries like WinForms and WPF is the sheer number of members that pop up in intellisense for a control. The Button type in WPF has around 300 members and a total of 9 levels of inheritance making it hard to find the useful members. Or to put it another way the inherited members occlude the useful members:

Button members

The picture above shows what you actually see in the code completion box in the editor, however for a button you’re probably more interested in the Click event, but that’s several pages away.

Disinherit Type Provider

As a thought experiment I’ve implemented an F# Type Provider that hides type members after a specified level of inheritance:

Disinherited button

In the picture above the members now almost fit in a single page. The last property, not visible here, is an extra property added by the type provider that provides all the hidden members for the instance:

Button instance

 

Type Provider Implementation

Underneath the covers the type provider takes an assembly name as a static parameter and optionally the level of inheritance to expose.

[<Literal>]
let name = @"PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
type WPF = Disinherited< name , level=1 >
let button = WPF.Button()

The provider then reflects over the types in the assembly and creates a proxy type as a provided type and exposes members that are declared within the specified inheritance level.

To provide the member we simply create a corresponding provided member with an expression that evaluates to the underlying type’s member:

    let addMember (def:ProvidedTypeDefinition) ty (mem:MemberInfo) =
        match mem.MemberType with
        | MemberTypes.Constructor ->
            let ci = mem :?> ConstructorInfo
            let c = ProvidedConstructor(toParams ci)
            c.InvokeCode <- fun args -> Expr.Coerce(Expr.NewObject(ci,args), typeof<obj>)
            def.AddMember(c)
        | MemberTypes.Field ->
            let fi = mem :?> FieldInfo
            let field = ProvidedField(mem.Name, fi.FieldType)
            def.AddMember(field)
        | MemberTypes.Property ->
            let pi = mem :?> PropertyInfo
            let prop = ProvidedProperty(mem.Name, pi.PropertyType) 
            prop.GetterCode <- fun args -> Expr.PropertyGet(Expr.Coerce(args.[0],ty), pi)
            def.AddMember(prop)
        | MemberTypes.Event ->
            let ei = mem :?> EventInfo
            let ev = ProvidedEvent(mem.Name, ei.EventHandlerType) 
            ev.AdderCode <- fun args -> Expr.Call(Expr.Coerce(args.Head,ty),ei.GetAddMethod(), args.Tail)
            ev.RemoverCode <- fun args -> Expr.Call(Expr.Coerce(args.Head,ty), ei.GetRemoveMethod(), args.Tail)
            def.AddMember(ev)
        | MemberTypes.Method ->
            let mi = mem :?> MethodInfo
            if not mi.IsSpecialName then
                let m = ProvidedMethod(mi.Name, toParams mi, mi.ReturnType)
                m.InvokeCode <- fun args -> Expr.Call(Expr.Coerce(args.Head,ty), mi, args.Tail)
                def.AddMember(m)
        | _ -> ()

The entire provider is implemented in around 100 lines of code, I’d be interested to hear if anybody else finds it useful :)

Source code

The source is available on GitHub: https://github.com/ptrelford/Disinherit

Comments (1) -

  • Chris Marisic

    12/10/2015 9:57:37 AM |

    I strongly agree with your intent here. So many things show up on core types when you hit dot, the extremely majority of them being never what you want.

Pingbacks and trackbacks (3)+

Comments are closed