3
\$\begingroup\$
FileVersionInfo.GetVersionInfo(typeof(Program).Assembly.CodeBase.Replace("file:///", "")).ProductVersion

That code, is a one-liner, but has quite a lot of steps for it to return the File Version. Is there maybe a AppDomain way to get the File Version? (Or in general a shorter way)

(The code DOES work - I dont know why its being voted to close as "Broken Code")

t3chb0t
44.7k9 gold badges84 silver badges190 bronze badges
asked Jul 10, 2017 at 1:01
\$\endgroup\$
6
  • 4
    \$\begingroup\$ I don't understand why this was closed. Though the purpose is simple, it seems real and legit. I reopened now. \$\endgroup\$ Commented Jul 10, 2017 at 7:42
  • \$\begingroup\$ Looks more like a Stackoverflow question to me (and a duplicate at that: stackoverflow.com/questions/6493715/… among others). There's hardly anything to review here. Either way, you can use Location instead of CodeBase.Replace(...), and apparently there's a way to obtain it from an assembly attribute. \$\endgroup\$ Commented Jul 10, 2017 at 9:00
  • \$\begingroup\$ @PieterWitvoet And as you can see, the SO link shows you how to do the above with even more steps, yet my code is still pretty long. Is there no AppDomain.Version or anything? \$\endgroup\$ Commented Jul 10, 2017 at 9:09
  • \$\begingroup\$ Judging by those answers, no, it doesn't get much simpler than this. Some frameworks or libraries may make this a little easier, but why bother? It's only a few steps. If you need to use this a lot then you can always make a utility function for it. \$\endgroup\$ Commented Jul 10, 2017 at 9:18
  • \$\begingroup\$ @PieterWitvoet Just seems odd that the shortest way requires stuff like typeof and .Replace :l \$\endgroup\$ Commented Jul 10, 2017 at 9:22

1 Answer 1

5
\$\begingroup\$

This one line might be left as-is, it's short and simple enough to be fully understood at first sight (especially if outer method has a meaningful name). However it has the disadvantage to be hard to test because it is doing 3 things:

  • Getting the assembly which is the entry point of your application.
  • Resolving the full path of that assembly.
  • Reading the version information of that assembly.

In an ideal world I'd probably want to test them in isolation but it might happen only when I will have a bug there, for sure I want to reuse this code elsewhere then I will introduce separate methods.

public static FileVersionInfo GetProductVersion()
 => FileVersionInfo.GetVersionInfo(GetEntryAssemblyPath()).ProductVersion;
public static string GetEntryAssemblyPath()
 => GetAssemblyPath(Assembly.GetEntryAssembly());

I changed typeof(Program).Assembly to Assembly.GetEntryAssembly() because if I move it into a separate DLL then I won't have Program. Note, however, that they may differ (see later).

What I'd really change is the way you obtain the path. Hard-coding file:// is tricky and unnecessary and you do not handle URL escapes. Moreover it will return an incorrect result if assembly has been loaded with Load(). It's easier to write a slightly more correct version:

public static string GetAssemblyPath(Assembly assembly)
 => Uri.UnescapeDataString(new UriBuilder(assembly.Location).Path);

Note that here I'm accessing Assembly.Location but it throws an exception for dynamically generated assembly and it's an empty string for assemblies loaded with Load(). A "better" version should then handle both cases:

public static string GetAssemblyPath(Assembly assembly)
{
 if (assembly.IsDynamic || assembly.Location == String.Empty)
 return ""
 return Uri.UnescapeDataString(new UriBuilder(assembly.Location).Path);
}

Little bit better but if we want to make GetEntryAssemblyPath() a reusable method we should do better because the entry assembly is not always defined (web applications come to my mind in this moment but I kind-of remember about other issues in hosted environments). The choice between Assembly.CodeBase and Assembly.Location isn't important here but in another context you should check for Assembly.Location == String.Empty also when using Assembly.CodeBase.

In your tests you can now verify GetAssemblyPath() in isolation and you may add separate tests for other functions when you will find an issue there (or when their code will be more complicate).

Last pedantic note: we're not checking if file effectively contains the required information and, usually, for assemblies it's also possible to read ProductInformationalVersionAttribute (which might be different).

answered Jul 10, 2017 at 12:23
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.