TL:DR; is it bad to make a struct where the value does something to initialize itself at the beginning of get
for the struct itself (no public properties, but any comparison/etc. executes initialization), and if so why?
I'm wondering how bad an idea it is to generate what is effectively the default value for a struct in an effectively-immutable way. I've read about how bad it is to have a mutable struct, etc, but what if you have a struct with no public properties - it in itself is the value it represents - and that value corresponds to some external immutable resource?
For example, consider the following struct:
using System;
public struct Computer
{
private string _name;
private string _domain;
private bool _isInitialized;
public static Computer Parse(string name)
{
if (string.IsNullOrEmpty(name))
{
throw new ArgumentNullException("name");
}
var result = new Computer();
result._name = string.Copy(name);
result._domain = string.Empty;
result._isInitialized = true;
return result;
}
public static Computer Parse(string name, string domain)
{
if (string.IsNullOrEmpty(name))
{
throw new ArgumentNullException("name");
}
var result = new Computer();
result._name = string.Copy(name);
if (_domain == null) { result._domain = string.Empty; }
else { result._domain = string.Copy(domain); }
result._isInitialized = true;
return result;
}
private void Initialize()
{
if (!_isInitialized)
{
var source = System.Net.NetworkInformation.IPGlobalProperties.GetIPGlobalProperties();
_name = source.HostName;
_domain = source.DomainName;
_isInitialized = true;
}
}
public override string ToString()
{
Initialize();
if (!string.IsNullOrEmpty(_domain)) {
return _name + "." + _domain;
}
else {
return _name;
}
}
public override bool Equals(object other)
{
Initialize();
if (other is Computer)
{
var otherComputer = (Computer)other;
return _name.Equals(otherComputer._name, StringComparison.OrdinalIgnoreCase) &&
_domain.Equals(otherComputer._domain, StringComparison.OrdinalIgnoreCase);
}
else
{
return false;
}
}
// additional comparison methods omitted.
}
As seen, any operation for comparison, etc. of the Computer
entity will result in initialization - effectively, observing the Computer
will result in it no longer being a 0-byte value.
Why would I do such a thing? I want something akin to an immutable value type that is, by default, representative of an actual value - as opposed to a truly default struct that would be 0 bytes. I can then use such a thing as a default value to a parameter: public void DoSomething(Computer computer = default)
and know that default
means the local device - if this were not a ValueType
, I would have to pass in null
as the default for the parameter as it's impossible to have a constant reference type (i.e. can't have public void DoSomething(string computerName = Environment.MachineName)
).
What this means is that the true "default" is never really observed - and the struct can never be read as a 0 byte value, which I have read is what a value type's default
essentially is.
Why shouldn't I do this - or is it totally OK? Are there instances in well-known code where this practice is applied?