3
\$\begingroup\$

I am working on an app that will interact with assembly (app) versions. I created a struct for that, along with an exception for parsing error.

Here's the code:

    [Serializable]
    public class VersionParseException : Exception
    {
        public VersionParseException()
        { }

        public VersionParseException(string message)
            : base(message)
        { }

        public VersionParseException(string message, Exception innerException)
            : base(message, innerException)
        { }

        protected VersionParseException(SerializationInfo info, StreamingContext context)
            : base(info, context)
        { }

    }

    public struct AppVersion
    {
        int _majorVersion;
        public int MajorVersion
        {
            get { return _majorVersion; }
            set
            {
                _majorVersion = value;
            }
        }

        int _minorVersion;
        public int MinorVersion
        {
            get { return _minorVersion; }
            set
            {
                _minorVersion = value;
            }
        }

        int _buildNumber;
        public int BuildNumber
        {
            get { return _buildNumber; }
            set
            {
                _buildNumber = value;
            }
        }

        int _revision;
        public int Revision
        {
            get { return _revision; }
            set
            {
                _revision = value;
            }
        }

        public string Version
        {
            get { return MajorVersion.ToString() + "." + MinorVersion.ToString() + "." + BuildNumber.ToString() + "." + Revision.ToString(); }
            set
            {
                try
                {
                    MajorVersion = int.Parse(value.Split('.')[0]);
                    MinorVersion = int.Parse(value.Split('.')[1]);
                    BuildNumber = int.Parse(value.Split('.')[2]);
                    Revision = int.Parse(value.Split('.')[3]);
                }
                catch (Exception exception)
                {
                    throw new VersionParseException("Invalid App Version", exception);
                }
            }
        }
    }

Is there any better way to handle AppVersions and exceptions occurring in the process of using it?

And here's an example of using the struct:

AppVersion version1;
version1.Version = "0.500.965.201";
Console.WriteLine(version1.MinorVersion); // outputs "500"
version1.BuildNumber = 753;
Console.WriteLine(version1.Version);      // outputs "0.500.753.201"

Another example on throwing the exception:

AppVersion version2;
version2.Version = "0.blabla.987.test"; //Throws VersionParseException
\$\endgroup\$
5
\$\begingroup\$

I am working on an app that will interact with assembly (app) versions

There's a class, System.Version, that might be suitable for your needs. System.Version:

Represents the version number of an assembly, operating system, or the common language runtime.

It has Major, Minor, Build, and Revision properties.

If you still want to roll your own, my advice is to

  • Make AppVersion immutable
  • Be stricter about the values you allow
  • Implement IEquatable<AppVersion>
  • Consider implementing IComparable<AppVersion>

In general, you should avoid mutable structs.

From MSDN

X DO NOT define mutable value types.

Mutable value types have several problems. For example, when a property getter returns a value type, the caller receives a copy. Because the copy is created implicitly, developers might not be aware that they are mutating the copy, and not the original value.

See also: Why are mutable structs “evil”?


With the code as written, this works just fine:

appVersion.Version = "   +123 . -4236 . +0 . -789";

Use the overloads of int.Parse/int.TryParse that take a NumberStyles argument. If you provide constructors as suggested below, check that the values passed in are non-negative.


From MSDN

✓ DO implement IEquatable<T> on value types.

The Object.Equals method on value types causes boxing, and its default implementation is not very efficient, because it uses reflection. Equals can have much better performance and can be implemented so that it will not cause boxing.


I find it hard to imagine not ever wanting to compare app versions, so you will probably want to implement IComparable<AppVersion>.


I would recommending getting rid of the Version property entirely. Instead, provide one or more constructors like

public AppVersion(int majorVersion, int minorVersion)

public AppVersion(int majorVersion, int minorVersion, int buildNumber)

...

and then provide static methods AppVersion Parse(string) and bool TryParse(string, out AppVersion).


Also, you should be aware of the existence of auto-implemented properties.

\$\endgroup\$
2
  • \$\begingroup\$ I'm not very experienced in .NET, how do I go about implementing IEquatable and IComparable? \$\endgroup\$ – Walid Nawfal Sabihi Nov 16 '15 at 18:51
  • \$\begingroup\$ And if I use such contructors and remove the version property, you think I should implement my own toString () to AppVersion then right? \$\endgroup\$ – Walid Nawfal Sabihi Nov 16 '15 at 18:53
3
\$\begingroup\$

Since you are already throwing your own exception, you should use int.TryParse instead. I know, you want to have an InnerException on your VersionException, but consider: what value does it add? It also only indicates a problem with one element, whereas if you use int.TryParse, you can indicate to the user all the elements that had problems. In my opinion, that's a much more helpful exception.

Plus, Exception catching and throwing is slow. There's no need to catch an exception if you can avoid it completely.

You should also consider adding the appropriate attributes for DataContractSerializer (and the like). I find that I use this (and DataContractJsonSerializer way more often than any others, but they require special attribute management.

\$\endgroup\$
5
  • \$\begingroup\$ But what should I do incase TryParse fail? Throw exception? Set version data to null or 0s? \$\endgroup\$ – Walid Nawfal Sabihi Nov 15 '15 at 18:29
  • 1
    \$\begingroup\$ That's when you throw your custom exception. You can go through the int.TryParse for all four elements, and then throw one exception to indicate all of the TryParse calls that failed. \$\endgroup\$ – Der Kommissar Nov 15 '15 at 18:31
  • \$\begingroup\$ Oh, I see. thank you for the suggestions. you can never have enough knowledge! \$\endgroup\$ – Walid Nawfal Sabihi Nov 15 '15 at 19:00
  • \$\begingroup\$ @WalidNawfalSabihi Exactly, some people would have been upset by this, but I'm glad your not one of that some! :) \$\endgroup\$ – Der Kommissar Nov 15 '15 at 19:03
  • \$\begingroup\$ I hope so... ;) \$\endgroup\$ – Walid Nawfal Sabihi Nov 15 '15 at 19:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.