lithiumfrost
lithiumfrost

Reputation: 83

MailMesage F# extension type to serialize to MIME string

I am trying to get MailMessage in .NET to return a string of the MIME message, but this is not provided in the delivered class. There's another excellent answer on how to create a C# extension method to monkey patch the class to provide the functionality. I am trying to port that to F# with a type extension, but I am getting hung up on how to provide the parameters (especially given that one of them is an F# keyword).

Would really appreciate an explanation of how this is done properly with the answer.

Here's what I have gotten so far (this will, of course, not currently compile):

open System.Net.Mail

module MailExtension =
    type MailMessage with 
        member this.toEml mail =
            let stream = new MemoryStream();
            let mailWriterType = mail.GetType().Assembly.GetType("System.Net.Mail.MailWriter");
            let mailWriter = Activator.CreateInstance(
                                type: mailWriterType,
                                bindingAttr: BindingFlags.Instance | BindingFlags.NonPublic,
                                binder: null,
                                args: new object[] { stream },
                                culture: null,
                                activationAttributes: null)

            mail.GetType().InvokeMember(
                                name: "Send",
                                invokeAttr: BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.InvokeMethod,
                                binder: null,
                                target: mail,
                                args: new object[] { mailWriter, true, true });


            Encoding.UTF8.GetString(stream.ToArray());

Upvotes: 1

Views: 131

Answers (1)

Alex Netkachov
Alex Netkachov

Reputation: 13522

Here is a few hints on how to translate C# to F#:

  • the ; is not longer required
  • use "use" instead of "let" for IDisposables
  • for arrays use [| member1, member2 |]
  • for named parameters use name=value
  • wrap keywords in names in ``name``
  • bitwise operators are ||| and &&&
  • use instance name instead of argument

Code that compiles:

open System
open System.IO
open System.Net.Mail
open System.Reflection
open System.Text

module MailExtension =
    type MailMessage with 
        member this.toEml () =
            use stream = new MemoryStream()
            let mailWriterType = this.GetType().Assembly.GetType("System.Net.Mail.MailWriter")
            let mailWriter = Activator.CreateInstance(
                                ``type`` = mailWriterType,
                                bindingAttr = (BindingFlags.Instance ||| BindingFlags.NonPublic),
                                binder = null,
                                args = [| stream |],
                                culture = null,
                                activationAttributes = null)

            this.GetType().InvokeMember(
                                name = "Send",
                                invokeAttr = (BindingFlags.Instance ||| BindingFlags.NonPublic ||| BindingFlags.InvokeMethod),
                                binder = null,
                                target = this,
                                args = [| mailWriter, true, true |])


            Encoding.UTF8.GetString(stream.ToArray())

Then use:

open MailExtension
let m = new MailMessage()
m.toEml () |> ignore

Upvotes: 3

Related Questions