Skip to content

Commit 3972776

Browse files
authored
ICustomFormatter F# snippets (dotnet#7765)
1 parent 5c577d3 commit 3972776

File tree

3 files changed

+152
-0
lines changed

3 files changed

+152
-0
lines changed
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
// <Snippet1>
2+
open System
3+
open System.Globalization
4+
open System.Numerics
5+
6+
type BinaryFormatter() =
7+
interface IFormatProvider with
8+
// IFormatProvider.GetFormat implementation.
9+
member this.GetFormat(formatType: Type) =
10+
// Determine whether custom formatting object is requested.
11+
if formatType = typeof<ICustomFormatter> then
12+
this
13+
else
14+
null
15+
16+
interface ICustomFormatter with
17+
// Format number in binary (B), octal (O), or hexadecimal (H).
18+
member this.Format(format, arg: obj, formatProvider: IFormatProvider) =
19+
// Handle null or empty format string, string with precision specifier.
20+
let thisFmt =
21+
// Extract first character of format string (precision specifiers
22+
// are not supported).
23+
if String.IsNullOrEmpty format |> not then
24+
if format.Length > 1 then
25+
format.Substring(0, 1)
26+
else
27+
format
28+
else
29+
String.Empty
30+
31+
// Get a byte array representing the numeric value.
32+
let bytes =
33+
match arg with
34+
| :? sbyte as arg ->
35+
let byteString = arg.ToString "X2"
36+
Some [| Byte.Parse(byteString, NumberStyles.HexNumber) |]
37+
| :? byte as arg ->
38+
Some [| arg |]
39+
| :? int16 as arg ->
40+
BitConverter.GetBytes arg
41+
|> Some
42+
| :? int as arg ->
43+
BitConverter.GetBytes arg
44+
|> Some
45+
| :? int64 as arg ->
46+
BitConverter.GetBytes arg
47+
|> Some
48+
| :? uint16 as arg ->
49+
BitConverter.GetBytes arg
50+
|> Some
51+
| :? uint as arg ->
52+
BitConverter.GetBytes arg
53+
|> Some
54+
| :? uint64 as arg ->
55+
BitConverter.GetBytes arg
56+
|> Some
57+
| :? bigint as arg ->
58+
arg.ToByteArray()
59+
|> Some
60+
| _ ->
61+
None
62+
let baseNumber =
63+
match thisFmt.ToUpper() with
64+
// Binary formatting.
65+
| "B" -> Some 2
66+
| "O" -> Some 8
67+
| "H" -> Some 16
68+
// Handle unsupported format strings.
69+
| _ -> None
70+
71+
match bytes, baseNumber with
72+
| Some bytes, Some baseNumber ->
73+
// Return a formatted string.
74+
let mutable numericString = String.Empty
75+
for i = bytes.GetUpperBound 0 to bytes.GetLowerBound 0 do
76+
let byteString = Convert.ToString(bytes[i], baseNumber)
77+
let byteString =
78+
match baseNumber with
79+
| 2 ->
80+
String('0', 8 - byteString.Length) + byteString
81+
| 8 ->
82+
String('0', 4 - byteString.Length) + byteString
83+
// Base is 16.
84+
| _ ->
85+
String('0', 2 - byteString.Length) + byteString
86+
numericString <- numericString + byteString + " "
87+
numericString.Trim()
88+
| _ ->
89+
try
90+
this.HandleOtherFormats(format, arg)
91+
with :? FormatException as e ->
92+
raise (FormatException($"The format of '{format}' is invalid.", e))
93+
94+
member private this.HandleOtherFormats(format, arg: obj) =
95+
// <Snippet3>
96+
match arg with
97+
| :? IFormattable as arg ->
98+
arg.ToString(format, CultureInfo.CurrentCulture)
99+
| null ->
100+
String.Empty
101+
| _ ->
102+
string arg
103+
// </Snippet3>
104+
// </Snippet1>
105+
106+
// <Snippet2>
107+
Console.WindowWidth <- 100
108+
109+
let byteValue = 124uy
110+
// <Snippet4>
111+
String.Format(BinaryFormatter(), "{0} (binary: {0:B}) (hex: {0:H})", byteValue)
112+
|> printfn "%s"
113+
// </Snippet4>
114+
115+
let intValue = 23045
116+
String.Format(BinaryFormatter(), "{0} (binary: {0:B}) (hex: {0:H})", intValue)
117+
|> printfn "%s"
118+
119+
let ulngValue = 31906574882uL
120+
String.Format(BinaryFormatter(), "{0}\n (binary: {0:B})\n (hex: {0:H})", ulngValue)
121+
|> printfn "%s"
122+
123+
let bigIntValue = BigInteger.Multiply(Int64.MaxValue, 2)
124+
String.Format(BinaryFormatter(), "{0}\n (binary: {0:B})\n (hex: {0:H})", bigIntValue)
125+
|> printfn "%s"
126+
127+
// The example displays the following output:
128+
// 124 (binary: 01111100) (hex: 7c)
129+
// 23045 (binary: 00000000 00000000 01011010 00000101) (hex: 00 00 5a 05)
130+
// 31906574882
131+
// (binary: 00000000 00000000 00000000 00000111 01101101 11000111 10110010 00100010)
132+
// (hex: 00 00 00 07 6d c7 b2 22)
133+
// 18446744073709551614
134+
// (binary: 00000000 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111110)
135+
// (hex: 00 ff ff ff ff ff ff ff fe)
136+
// </Snippet2>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<OutputType>Exe</OutputType>
4+
<TargetFramework>net6.0</TargetFramework>
5+
</PropertyGroup>
6+
7+
<ItemGroup>
8+
<Compile Include="binaryformatter.fs" />
9+
</ItemGroup>
10+
</Project>

xml/System/ICustomFormatter.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,13 @@
6767
The following example implements <xref:System.ICustomFormatter> to allow binary, octal, and hexadecimal formatting of integral values. In this example, a single class, `IBinaryFormatter`, implements both <xref:System.ICustomFormatter> and <xref:System.IFormatProvider>. Its <xref:System.IFormatProvider.GetFormat%2A?displayProperty=nameWithType> method determines whether the `formatType` parameter represents an <xref:System.ICustomFormatter> type. If it does, `BinaryFormatter` returns an instance of itself; otherwise, it returns `null`. Its <xref:System.ICustomFormatter.Format%2A?displayProperty=nameWithType> implementation determines whether the format parameter is one of the three supported format strings ("B" for binary, "O" for octal, and "H" for hexadecimal) and formats the `arg` parameter appropriately. Otherwise, if `arg` is not `null`, it calls the `arg` parameter's <xref:System.IFormattable.ToString%2A?displayProperty=nameWithType> implementation, if one exists, or its parameterless `ToString` method, if one does not. If `arg` is `null`, the method returns <xref:System.String.Empty?displayProperty=nameWithType>.
6868
6969
:::code language="csharp" source="~/snippets/csharp/System/ICustomFormatter/Overview/binaryformatter.cs" id="Snippet1":::
70+
:::code language="fsharp" source="~/snippets/fsharp/System/ICustomFormatter/Overview/binaryformatter.fs" id="Snippet1":::
7071
:::code language="vb" source="~/snippets/visualbasic/VS_Snippets_CLR_System/system.icustomformatter.class/vb/binaryformatter.vb" id="Snippet1":::
7172
7273
`BinaryFormatter` can then be used to provide custom formatting by passing a `BinaryFormatter` object as the `provider` parameter of the <xref:System.String.Format%2A> method, as the following example shows.
7374
7475
:::code language="csharp" source="~/snippets/csharp/System/ICustomFormatter/Overview/binaryformatter.cs" id="Snippet2":::
76+
:::code language="fsharp" source="~/snippets/fsharp/System/ICustomFormatter/Overview/binaryformatter.fs" id="Snippet2":::
7577
:::code language="vb" source="~/snippets/visualbasic/VS_Snippets_CLR_System/system.icustomformatter.class/vb/binaryformatter.vb" id="Snippet2":::
7678
7779
]]></format>
@@ -160,6 +162,7 @@
160162
<xref:System.ICustomFormatter.Format%2A?displayProperty=nameWithType> is a callback method. It is called by a method that supports custom formatting, such as <xref:System.String.Format%28System.IFormatProvider%2CSystem.String%2CSystem.Object%5B%5D%29?displayProperty=nameWithType> or <xref:System.Text.StringBuilder.AppendFormat%28System.IFormatProvider%2CSystem.String%2CSystem.Object%5B%5D%29?displayProperty=nameWithType>. The implementation is called once for each format item in a [composite format string](/dotnet/standard/base-types/composite-formatting). For example, in the following statement, the <xref:System.ICustomFormatter.Format%2A?displayProperty=nameWithType> method is called three times.
161163
162164
:::code language="csharp" source="~/snippets/csharp/System/ICustomFormatter/Overview/binaryformatter.cs" id="Snippet4":::
165+
:::code language="fsharp" source="~/snippets/fsharp/System/ICustomFormatter/Overview/binaryformatter.fs" id="Snippet4":::
163166
:::code language="vb" source="~/snippets/visualbasic/VS_Snippets_CLR_System/system.icustomformatter.class/vb/binaryformatter.vb" id="Snippet4":::
164167
165168
The `arg` parameter is the object in the object list whose zero-based position corresponds to the index of a particular format item.
@@ -171,6 +174,7 @@
171174
Your implementation of the <xref:System.ICustomFormatter.Format%2A> method must include the following functionality so the .NET Framework can provide formatting you do not support. If your format method does not support a format, determine whether the object being formatted implements the <xref:System.IFormattable> interface. If it does, invoke the <xref:System.IFormattable.ToString%2A?displayProperty=nameWithType> method of that interface. Otherwise, invoke the default <xref:System.Object.ToString%2A?displayProperty=nameWithType> method of the underlying object. The following code illustrates this pattern.
172175
173176
:::code language="csharp" source="~/snippets/csharp/System/ICustomFormatter/Overview/binaryformatter.cs" id="Snippet3":::
177+
:::code language="fsharp" source="~/snippets/fsharp/System/ICustomFormatter/Overview/binaryformatter.fs" id="Snippet3":::
174178
:::code language="vb" source="~/snippets/visualbasic/VS_Snippets_CLR_System/system.icustomformatter.class/vb/binaryformatter.vb" id="Snippet3":::
175179
176180
@@ -179,11 +183,13 @@
179183
The following example implements <xref:System.ICustomFormatter> to allow binary, octal, and hexadecimal formatting of integral values. Its <xref:System.ICustomFormatter.Format%2A?displayProperty=nameWithType> implementation determines whether the format parameter is one of the three supported format strings ("B" for binary, "O" for octal, and "H" for hexadecimal) and formats the `arg` parameter appropriately. Otherwise, if `arg` is not `null`, it calls the `arg` parameter's <xref:System.IFormattable.ToString%2A?displayProperty=nameWithType> implementation, if one exists, or its parameterless `ToString` method, if one does not. If `arg` is `null`, the method returns <xref:System.String.Empty?displayProperty=nameWithType>.
180184
181185
:::code language="csharp" source="~/snippets/csharp/System/ICustomFormatter/Overview/binaryformatter.cs" id="Snippet1":::
186+
:::code language="fsharp" source="~/snippets/fsharp/System/ICustomFormatter/Overview/binaryformatter.fs" id="Snippet1":::
182187
:::code language="vb" source="~/snippets/visualbasic/VS_Snippets_CLR_System/system.icustomformatter.class/vb/binaryformatter.vb" id="Snippet1":::
183188
184189
`BinaryFormatter` can then be used to provide custom formatting by passing a `BinaryFormatter` object as the `provider` parameter of the <xref:System.String.Format%2A> method, as the following example shows.
185190
186191
:::code language="csharp" source="~/snippets/csharp/System/ICustomFormatter/Overview/binaryformatter.cs" id="Snippet2":::
192+
:::code language="fsharp" source="~/snippets/fsharp/System/ICustomFormatter/Overview/binaryformatter.fs" id="Snippet2":::
187193
:::code language="vb" source="~/snippets/visualbasic/VS_Snippets_CLR_System/system.icustomformatter.class/vb/binaryformatter.vb" id="Snippet2":::
188194
189195
]]></format>

0 commit comments

Comments
 (0)