Today has been day 6789
Counting the days since 1-Jan-2000, as in the automatic versioning for .Net files in the "1.0.*.*" format.
Diary, commentary, reviews, snippets to preserve on-line
Counting the days since 1-Jan-2000, as in the automatic versioning for .Net files in the "1.0.*.*" format.
I've just had an interesting run-in with a little-advertised and not backwards-compatible feature in .net 4.6 and up, and how it affects FIPS-compliance, for those of us who have to worry about such things.
You see, in the recent .net versions (unlike the older ones), the selection of the default implementation toggles on whether the machine has HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Lsa\FipsAlgorithmPolicy\[Enabled] set non-zero or not (or the equivalent group policy), and will select a FIPS-certified implementation for SHA256 (and SHA384 and SHA512), and, interestingly only for those flavours of SHA-2, using exactly the same criterion as is used to make the non-compliant implementations raise an exception on construction. So, assuming no app or machine level overrides, the matrix looks like this:
| no FIPS enforcement | FIPS enforced | |
|---|---|---|
| .net 4.6 and up (Dev machine) | SHA###Managed OK | SHA###Cng "Works on my machine." |
| .net 4.5.2 or earlier (Slow-moving customer environment) | SHA###Managed OK | SHA###Managed KABOOM! |
The end-stop selection of algorithm defaults (where not overridden by a specific [class].Create()) in .net 4.5.2 are drawn from mscorlib alone, many FIPS-compliant, with the notable exceptions being the "new" (SHA-2, or AES which is present only by the original name of Rijndael), or old-and-deprecated (like MD5) algorithms. As in most cases, [DerivedClass].Create() is just a synonym for [BaseAlgorithm].Create(), you can get a false sense of security here -- SHA256CryptoServiceProvider.Create() will equally spit out an instance of SHA256Managed in three of the cases above, and SHA256Cng in the fourth ("works on my machine").
TL;DR -- if you have to worry about FIPS, don't use [Algorithm].Create(), but select the one you mean by calling its constructor explicitly.
So, you have some code that looks like this
private static bool Match(int item, int? target)
{
if (target.HasValue)
{
return item == target;
}
return false;
}How many tests do you need to write to get 100% branch coverage?
If you answered "two -- one with a value, one without", you'd be as surprised as I was when I tried it.
It turns out that the implicit extraction of the value of the nullable value contains its own HasValue check, and the IL looks like
IL_0000: nop
IL_0001: ldarga.s target
IL_0003: call instance bool valuetype [mscorlib]System.Nullable\u00601::get_HasValue()
IL_0008: ldc.i4.0
IL_0009: ceq
IL_000b: stloc.1
IL_000c: ldloc.1
IL_000d: brtrue.s IL_002b
IL_000f: nop
IL_0010: ldarg.0
IL_0011: stloc.2
IL_0012: ldarg.1
IL_0013: stloc.3
IL_0014: ldloc.2
IL_0015: ldloca.s CS$0$0003
IL_0017: call instance !0 valuetype [mscorlib]System.Nullable\u00601::GetValueOrDefault()
IL_001c: bne.un.s IL_0027
IL_001e: ldloca.s CS$0$0003
IL_0020: call instance bool valuetype [mscorlib]System.Nullable\u00601::get_HasValue()
IL_0025: br.s IL_0028
IL_0027: ldc.i4.0
IL_0028: stloc.0
IL_0029: br.s IL_002f
IL_002b: ldc.i4.0
IL_002c: stloc.0
IL_002d: br.s IL_002f
IL_002f: ldloc.0
IL_0030: ret Roughly, it goes "get a value, or the default, and test that; if not equal, accept that, otherwise only accept the equality if the nullable had a value."
If you write
private static bool Match(int item, int? target)
{
if (target.HasValue)
{
return item == target.Value;
}
return false;
}then there is no compiler-generated branch for you to be caught by -- and it's probably slightly better coding practise, anyway.
People who come to this blog for the technical posts may have noticed that those rather dried up over the last couple of years. There was a highly boring reason for this -- having built a little guerilla build server with one of my colleagues, it suddenly took over the world, despite our best intentions. This meant an unexpected amount of DevOps work keeping it running, and continuing to develop what had been first intended to be just a system for our team into one that covered multiple sites in different geos.
Fortunately, success was eventually rewarded by another team writing a competing system, to which my response was "Thank you, very much!" and I have at last been able to shed that particular monkey from my back. In time, that period will become a source of many war stories, but there is one particular thing that I did notice.
The key "new thing" in this system was to chain together the individual component builds that were the elements provided to the formal build process, so we could get to system-testable outputs more quickly, in a manner that could work equally on the build server and the developer's local machine. This, of course, meant that developers would at times see their builds fail in components which they were not familiar with, and then come to the two of us juggling the server (and who might be equally unfamiliar with the failing component) for a resolution.
Some of the time it turned out to be some corner case we hadn't considered in our orchestration process, but often enough it was something environmental that precipitated an otherwise normal failure in the MSBuild based process. It's just that a normal failure ends up with yards of error messages, redoubled by being screaming red text when it's in a console window on your desktop. Something like:
D:\Source\Feature\path\to\component\Library4
\Library4.csproj(##, #): error XXX####: Something went wrong
Done Building Project "D:\Source\Feature\path\to\component\Library4
\Library4.csproj" (default targets) -- FAILED.
Done Building Project "D:\Source\Feature\path\to\component\Library3
\Library3.csproj" (default targets) -- FAILED.
Done Building Project "D:\Source\Feature\path\to\component\Library2a
\Library2a.csproj" (default targets) -- FAILED.
Done Building Project "D:\Source\Feature\path\to\component\Library2
\Library2.csproj" (default targets) -- FAILED.
Done Building Project "D:\Source\Feature\path\to\component\Library1
\Library1.csproj.metaproj" (default targets) -- FAILED.
Done Building Project "D:\Source\Feature\path\to\component\Component
.sln" (default targets) -- FAILED.
Done Building Project "D:\Source\Feature\path\to\component\Build\Buil
dAll.proj" (FullBuild target(s)) -- FAILED.
Build FAILED.
[105 lines of other errors omitted]
"D:\Source\Feature\path\to\component\Build\BuildAll.proj" (FullB
uild target) (1) ->
"D:\Source\Feature\path\to\component\Component.sln
" (default target) (2) ->
"D:\Source\Feature\path\to\component\Library1\Library1.
csproj.metaproj" (default target) (15) ->
"D:\Source\Feature\path\to\component\L
ibrary2\Library2.csproj" (default target) (16) ->
"D:\Source\Feature\path\to\component\L
ibrary.2a\Library2a.csproj" (default target) (18) ->
"D:\Source\Feature\path\to\component\Library3
\Library3.csproj" (default target) (20) ->
"D:\Source\Feature\path\to\component\Library4
\Library4" (default target) (19:2) ->
D:\Source\Feature\path\to\component\Library4
\Library4.csproj(##, #): error XXX####: Something went wrong
0 Warning(s)
7 Error(s)This, it turns out, is enough to make even many quite senior developers throw their hands up in horror, when it happens in an unfamiliar piece of code -- even though, a few hundred lines earlier, there will usually be an obvious root cause, maybe something quite blatant, like:
PreBuildEvent: PowerShell.exe -File "D:\Source\Feature\Path\to \component\Scripts\Do-Something.ps1" [arguments] The argument 'D:\Source\Feature\Path\to\component\Scripts\Do- Something.ps1' to the -File parameter does not exist. Provide the path to an existing '.ps1' file as an argument to the -File parameter.
which simply wasn't emitted as an error or warning in the MSBuild meaning of the terms, and so wasn't highlighted, or re-iterated, but which would be the start of what would become a cascade of FAILED and Error red text.
This phenomenon of a failure log ending with things going horribly wrong, but with a seemingly innocuous line much earlier that indicates when things started to go bad is not just restricted to MSBuild logs, or even build logs in general.
And that is the nice case. There can be worse. Sometimes the error messages at the end may be entirely unrelated to what actually failed or at best be only tangentially related (e.g. tidying operations failing when what they are supposed to tidy didn't get created) -- so trying to reason from them can be a hiding to nothing.
So when confronted by a failure log that seems to conclude with notification that the sky is falling for no good reason, take a deep breath, refuse to panic, and just start at the top. You might not always be able to resolve the issue personally, but you'll most likely be able to provide a better problem report to the person who can.
This one caught the team at work this week; after having in the past had many reports of "memory leaks" that turned out to be nothing more than the GC twiddling its thumbs while gigabytes of trash accumulated.
Symptom: Intermittent errors, in retail builds only, for some flavours of input, where threads just died; but the code looked sane.
A lot of painful debugging later got us to a method that looked like (stripped to the essentials):
which looks pretty innocent.
And it works most of the time -- until foo.CreateBar doesn't return immediately. And at that point the fact that foo becomes eligible for finalization just as soon as the call to foo.CreateBar begins rises up to bite you, because you're now in a race with the GC. Notably, this is not like C++ where destruction happens only when control leaves the enclosing scope, and that is why it took a long time to figure out what was going on.
Knowing that this can happen, the point of all those odd GC related types and methods suddenly becomes apparent.
Having been bitten again recently by some code which could contain null as a meaningful value, I set down and put together my own variation on this theme. Unlike the first hit I got for "c# option type", which when faced with the question of whether you could have a Just null, went with "Yes." that on the basis of an example with meaningful nulls (getting the first element if any of a sequence that might contain nulls), I'm going to say that the whole motivation for such a type is to avoid the trap of meaningful nulls, and if you occasionally need a transient Maybe<Maybe<T>>, that should represent an edge case which you'd expect to need handle with care anyway.
The constraints of the C# language mean that there isn't a perfect representation -- we need a struct to avoid any null values of the null-eliminating type, but that means we can't inherit to distinguish cases with and without value, or initialize fields, which means we have to explicitly do at runtime what we would do by virtual method calls.
Inside the struct, I just build on the well known dodge of using null-object IEnumerable-as-Maybe idea; which also allows us to access the vast number of Enumerable extension methods to augment the type. So we start out with
The first two serve to move the generic from the type to the function name; then we have a series of conversions that invert or augment ones that we already have. Analogues of any further Enumerable extension methods desired can now be written in the form AsEnumerable.EnumerableExtensionMethodReturningIEnumerable().ToMaybe() -- the one that I see as most likely to see heavy use being OfType<T>() to conditionally extract the value as a subtype.
And C# extension methods are really monkey-patching.
Let's look at this example -- MSFT provided events and a handler mechanism; ElegantCode writes an extension method to do the necessary null handler check, and only when I include the ElegantCode namespace does the compiler see that myEvent.Fire(...) is a valid call. The MyExtensions type has disappeared from sight -- it doesn't appear as a useful type at any point; and the extension method only appears to the compiler when an object is named explioitly -- no implicit this from inside a type named in an extension method.
In contrast, let's consider the following Scala snippet (which parallels the types in my previous post)
If we build this with the (once again abandoned) .net compiler, then decompile, we get this C# code
The underlying mechanism by which the mixin (trait) is rendered -- static methods in a separate class holding the implementation -- is the same as for C# extension methods having the same effect (see previous post). The difference is that the trait type Stringify has appeared as a significant type : it's an interface, and the static methods are on a separate synthetic class.
The difference then continues into the consumers -- a type has to opt in to the trait explicitly
or, when we decompile,
and here is the trade-off. We have a significant type (which other types can consume), and we do get implicit this, but the trait has to be inherited to become available, and delegating methods are injected to provide implicit this. (Parenthetical note -- Scala makes the use of the token Library in the type definition unambiguous; another compiler-level difference)
Extension methods are completely backwards compatible -- you can use LINQ without having to change all the collection types in your code simply by including the namespace of the new extension methods, but you don't get a new orthogonal type that expresses "those classes expressing the extensions" -- the common class is the pre-existing one that the extension method has as its this parameter.
If you want to, you can manually add a marker interface to your C# code, and have your extension methods work on instances of that interface as the this parameter, in the same way as Stringify in the Scala example appears as an interface type. That provides something closer to mix-in behaviour that can be applied across otherwise unrelated types, but that is a separate manual step, and still leaves you with monkeypatched methods -- you can't declare the mix-in methods in that interface, and you still need explicit this, even though the IL is much the same in each case.
Posted at
10:28
No comments
:
This is another one of those rules that seems like a needless bit of pedantry, but there is a scenario where violating it can lead to unexpected behaviour, depending on your coding style. The documentation for the rule itself says about violations that
For new development, there are no known scenarios where you must exclude a warning from this rule. For shipping libraries, you might have to exclude a warning from this rule.
and most of the time it is harmless, but there is one wrinkle involving code sharing the namespace -- in the case I hit it, with some extension methods -- where it's not so obvious what you have to do to make your code compile.
I hit this when refactoring an old library to take one base class that managed a whole bunch of concerns into a more fine-grained set of classes (since most uses didn't need the full set of overheads, and some wanted to select a different set).
where the subclass has to be disambiguated from the namespace -- but that's mostly harmless as the shared systematic prefix means we don't need a using now. And this is where we set the little trap for ourselves when we add code to the namespace.
After refactoring the equivalent library code now looks like this
and in most cases we'd just change the namespace reference in the consuming code, and indicate the subject of the extension methods (the magic this.), like
and it would "just work". Instead, because of the way we've worked around the namespace clash above, it reports "The name 'AsString' does not exist in the current context"; because normally you'd already have an explicit using Systematic.Prefix.Library.Classic;, and the class declared just with its unqualified name,
And you still have to have that full using directive, to bring the extension methods into scope, even while the class itself still needs to be explicitly qualified.
Fortunately, R# will, as soon as you put the this. into place, prompt you to put the using directive in place.
When you really have to return two values (like a return state with various values for success and failure, and some more substantive sought object on success
), resorting to an out parameter seems so obvious, and the relevant static analysis rule does say
It is safe to suppress a warning from this rule. However, this design could cause usability issues.
So "real programmers" will reach for that, regarding the rule as one to mollycoddle junior developers; and will shy from the clunkiness of an anonymous tuple type (even now we have them in core .net languages). But there is still a point where they cause pain, and that is when the parameter is on an interface method, and you want to mock that method.
At this point you have to individually specify each invocation of the mocked method and its pre-computed out parameters -- even though with modern mocking frameworks like Moq, you can use a lambda to compute the return value based on arbitrary inputs, and make a normal mocked method algorithmic over expected inputs.
rather than
Upshot -- the usability issue is not that the caller has to look at two values like a success state, and then if relevant a different value, rather than look at fields in some carrying type; it's that your customers (in the Total Quality sense) will end up with rather more coupled and brittle unit tests when they try to abstract your component away through its interface.
Just in case anyone else has the same sort of bright idea that had me flummoxed for a while today...
I had put together a little tool for some C# meta-programming that mixed some reflection with some source code parsing driven through the StyleCop parser, and it was working nicely, but actually deploying it was a nuisance, because it wanted the StyleCop assemblies as well as just the .exe -- so I thought to myself "Let's ILMerge it all together, that should be nice and convenient."
And that's when the test harness stopped working, with no parsers found.
Chasing through the decompiled sources, it appears that the parser assembly StyleCop.CSharp.dll gets loaded by looking through other co-located assemblies with the same strong-naming key and whose name ends in ".dll" -- assumptions which merging into a .exe for distribution had just blown away.
Apart from having to manage what is no longer a single unit, however, the end result of using StyleCop for examining source (and in particular comments and suchlike not compiled into the assembly being reflected upon) in conjunction with reflection worked very nicely.
An initialisation problem I hit recently, a null pointer exception when initialising an object via an initialiser statement. This program is a simplified example --
which throws when initializing b.
Putting them together like this, it's obvious what I missed out in the second initializer : I'm not creating a new object to assign. But it's not obvious what the second case is actually doing, even though it compiles -- which in itself initially surprised me. So let's look at the IL and find out what that initialisation is actually doing...
which actually resolves to something like
So if we add a constructor and a sensible ToString to the type being initialised, thus
we see that the initialisation of a replaces the constructed values; whereas b adds to them.
Not sure how useful this is as the dictionary literal form only works in initializers like this, but it's another bit of living and learning.
More of what the compiler does without you realising it, only this time in a C# sample.
The absolutely simplest sort of method, which you can clearly completely cover by calling it once. Right?
That's what I thought until I tried running OpenCover over code rather like this, and it told me I had only covered one of two possible branches.
So naturally, I go "WTF?", and wonder whether it's the return or throw alternatives out of .First that it's alluding to and to be certain, crack open ILSpy, and see that first line expands to
which actually involves first caching the delegate corresponding to the lambda, if it hasn't been already; or using it on subsequent calls.
The release build is much the same; it lacks the initial nop, and the meaningless jump-to-next-instruction at offset 0x23.
Move the constant string out into the method, and make the lambda close over it
and the caching goes away : a new instance of ClassLibrary2.Class1/'<>c__DisplayClass1' gets created every time, debug or release. And with it goes the branch.
Alas not "Writing StyleCop rules for F#" (or should that be StyleFop?), which would be nice, but a true job of work...
I don't see much need for more StyleCop rules for C# (except one to harness the FxCop spellcheck facilities to scan comments, which would have to be done via much dodgy reflection to winkle out internal types, and would anyway give issues for being tied to 32-bit native executables), so haven't tried this before. But, like mountains, it's there...
So, you have StyleCop 4.5 or 4.6 installed, and start by creating an empty class library targeted at the .net 3.5 environment, with added references to the StyleCop and StyleCop.CSharp assemblies. The bare rule class looks like
and to go with it, add an XML file as an Embedded Resource with name My.Namespace.MyRules.xml -- first gotcha : the long name needs to be given in full because the default namespace doesn't get applied in the build like it would for C#. That can then be filled in as per the instructions in the StyleCop SDK documentation. Then just override the AnalyzeDocument method and go wild:
where I'm using the concept from the StyleCop+ rule SP2000 as an example of a new rule not yet present in the core tool.
Unlike FxCop, there is not a "one class = one rule" model here; the classes exist to group related rules, and you control how each scans the current source file of interest, and, indeed, how distinct they are in the code -- a closely related set of rules could be checked off a single scan, and in some ways StyleCop rules are more akin to the distinct named resolutions that can be defined within a single FxCop rule.
Another consequent difference from FxCop is how the object graph is visited. In FxCop, there are VisitXxx methods to override that will be called for every object of type Xxx, in an essentially stateless manner (your rule class is responsible for maintaining state between calls); here the equivalent methods are passed to a WalkXxx graph walker method as a callback, provide a mutable state object argument, and can signal through their return value whether the traversal should continue.
This approach of using a mutable object, needing down-casting, to carry inputs into each call, and contain the resultant outputs, feels a bit odd in a functional environment; fortunately it is possible to bypass this in many cases by representing the tree-walk as a seq and operating on that. For the trailing whitespace rule, where the offending lines are most easily found by scanning the raw source line by line, all we need the traversal for is to find an ICodeElement context object matching that line number. A simple walk of the code element tree looks like
which we apply to the document RootElement and from the result find the last (so most deeply nested, and hence most constrained) item whose Location has a span that includes the line number of interest (skip while the end is before the line of interest, take while the start includes the line, reverse, get head), the line number being a value that the operation can close over.
For deployment onto machines that don't have F# installed, static linkage of the runtime via the --standalone build flag can be used rather than having to explicitly drop the F# runtime assembly into the StyleCop folder (second gotcha).
Lots of people have re-invented the Maybe monad in C#, as a simple search will show, usually as a way of bypassing null checking if statements and to be able to write code like
But we have the tools given to us already if we observe that null has no type -- as we can see when
reports a length of zero!
So our Some<T> is just a length-1 IEnumerable<T>; and None<T> an empty one -- the null object pattern, in fact.
For the price of having to specify types at each stage -- as we would anyway have had to in the past for declaring intermediate the values to test against null -- we can use existing methods to finesse the null check. Returning to the borrowed example, it would be
The monadic return monad extraction operation is just FirstOrDefault(); which is fine for reference types. Value types are different in any case as we wouldn't be null checking those -- as the later steps in a chain though, stopping at the filtering to the value type and making an Any test may be preferred.
Looking at value types, switching to this example, the code looks like:
which leaks a little bit in that we have to do the monadic return monad extraction by hand at the end, but otherwise behaves exactly as the explicit monad implementation.
Unfortunately there is one major sticking point in getting the generation to work for F# -- local variables are declared as let mutable rather than as a ref type which means that the necessary closure cannot be made; this is rather stronger than the lack of support for CodeDefaultValueExpression, which can be fudged around, or for nested types (which just become mutually recursive), though with up-front decision as to the language to generate (rather than making the choice following the expression tree), we could replace problematic elements with snippets.
That aside, the main operation looks like this, similar to the previous examples
If we eschewed F# support entirely, and used a partial class, then we could skip the constructor and field declarations and provide the input by any other mechanism of our choice in a hand-written part.
Generating the closure classes is a simple matter -- especially as the names of the parameters can be used as fields directly without any sigils, provided that they never contain the @this or proxy name by convention. This makes the closure class approach slightly simpler than a snippet driven use of direct lambda syntax, where local variable names to hold out parameters would in general have to be generated so as not to clash with any of the arguments:
where the individual field declarations have to work around the decorations for out or ref, thus:
The generation of the proxy methods is equally mechanical
So, given a simple type
we generate
The F# code contains
Manually replacing this with
then shows up on the next line
as an error The mutable variable 'proxy' is used in an invalid way. Mutable variables cannot be captured by closures. Consider eliminating this use of mutation or using a heap-allocated mutable reference cell via 'ref' and '!'.; and as there are no readonly locals in the CLR -- it's all F# compiler magic that gives the illusion of same -- we're stuck with no control to tweak to make proxy immutable in the F# output.
The CodeDOM expression methods are stuck at the level of .net 2.0, and look unlikely to grow further support for further syntax outside of feeding it in as literal strings using the System.CodeDom.CodeSnippet* classes -- which takes away the cross-language spirit of the whole library.
So, what to do when we want to generate code containing lambdas?
Well, the obvious way is to strip away the syntactic sugar involved in the lambda/closure notation and generate explicitly what the compiler is doing for you under the covers anyway.
So, to take a case which is relevant to my interests, if we have a method (of static type Wrapper, say) --
public static T LogCall<T>(string name, Func<T> call)
that will perform some pre- and post- call logging around invoking the delegate; and we have a method
public int DoIt(int argument, out string result)
which we want to invoke through the LogCall method (considered as a decorator to the action). Writing this by hand, we would want a decorator type containing a matching method something like this:
which exposes the same interface, and delegates to the real logic in the contained object wrapped.
If we examine the code that is actually generated by compiling this, we see that the whole closure is replaced by a synthetic private inner class with a public field for each object closed over -- in this case all the arguments to the DoIt method, plus a this reference for the decorator -- and a method that stands for the lambda, which, generated names aside, looks like:
while the wrapping method looks like
And now, while the analogous F# code would probably return an int * string tuple, rather than an out parameter for multiple returns, the equivalent code snippet will do the job, just changing the types of the Func object; and the literal transposition of the C# code, out parameters and all, should still work, cross language.
In the more general case, of lambdas with arguments, then the method in the proxy type has the same arguments as the lambda needs; and in the case where no variables are being closed over e.g.
Func<int, int> square = x => x*x;
there is an optimization that the supporting method can be made static on the enclosing type, rather than requiring an object to hold the closure references.
In part 2, having simplified the problem, we can move on to using the CodeDOM to generate the LogCall decorating method and closure, given a MethodInfo.
Following up on the comment stream to an earlier post, a few little bits revealed by the investigations, with a little help from ILSpy:
yield break;
is the equivalent of
return;
inside an iterator function -- by which I mean that any silent exit from the method is compiled to the equivalent of an explicit yield break;, and
foreach(T x in y) { ... }on exit from the iteration calls the Dispose() method of the IEnumerator<T> it uses implicitly to iterate over the collection y; whereas, of course, explicit use of the enumerator MoveNext() and Current do not.
The behavioural constraints of IEnumerable are weak enough as it is, being silent on whether replayability is part of the contract or not; this inconsistent disposal (with the IDisposable interface being added only to the generic IEnumerator<T> and not the .net 1.x vanilla version) makes guessing behaviours even worse when using LINQ methods like Skip which does the dispose via using when the iteration is exhausted
and Take, which does it implicitly by the foreach route before the iterator has been completely drained
There are probably similar wonders waiting to be unearthed in the F# collections APIs as well.
As noted in the comments to the previous post, if you're not using the Window on an Enumerable that maintains its own internal state (so that the different IEnumerator yielded up each time will always be at the start of the iteration), it will loop forever. So if you're not using the two in conjunction for the purpose of "read a file in chunks for e.g. passing over a network" or similar, you want this variant:
where the Ratchet type ensures that the same IEnumerator instance is yielded up each time -- at this point you have to be rather Jesuitical about what is immutable anyway, the sequence that returns you different mutable objects or the one that returns you the same object you may already have mutated...
F# is less convenient for this one, though :(
Another thing that caught me on W2k3, 64-bit, recently, is that on some, but not all, systems if I have an application appname.exe which is C#, .net 3.5 sp1, compiled in AnyCPU mode, but installed to Program Files (x86), if you get the configuration file path by
var path = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).FilePath
it may report appname.config, as opposed to the more usual appname.exe.config as the file name.
And when that happens, it is not joking -- if your installer puts a file at appname.exe.config on such a system, your program will not read it. If you manually move the file to appname.config and restart the application, all will be well.
The internet hasn't been very forthcoming in either cause or remedy -- I did find several copies of a similar issue for 32-bit apps on Vista x64, but no resolution.
There's a similar bug report against .net 3.5 -- but by observation the issue only happens in a minority of cases. The only difference I've spotted between two machines where one shows the bug and the other doesn't is that the bug shows in the one with .net 3.0 and 3.5 sp1 not fully patched up to date -- but the patches don't touch any of the libraries that are likely to be involved (System.Configuration and dependencies).
I may have missed these in F#, and they don't seem at all obvious in C# standard libraries. A couple of handy functions/extension methods with simple tests using the Should.Fluent assertion library.
In F#:
In C#:
Previous instalments at Life before Blogging.
Forum Administrator : EvaGeeks.org — An Evangelion Fan Community
Copyright © Steve Gilham, 2003-2013
This work is licensed under a Creative Commons License.
Code snippets released under the WTFPL