Is anything like this possible? I'm assuming not, but it looks good to me:
class MyClass {
public int Foo {
get { return m_foo; }
set {
// Bounds checking, or other things that prevent the use
// of an auto-implemented property
m_foo = value;
}
// Put the backing field actually *in* the scope of the property
// so that the rest of the class cannot access it.
private int m_foo;
}
void Method() {
m_foo = 42; // Can't touch this!
}
}
Of course I know this syntax is incorrect, and this will not compile. It was hypothetical-future-C# for the sake of clearly portraying my idea. I apologize for the somewhat hypothetical question, but it is too specific for Programmers.SE.
Something like this could be implemented in the compiler that would serve one purpose: Only allow the property's get and set accessors to see the field, essentially allowing the property to be self-contained (as auto-implemented properties are) while allowing additional get/set logic.
The short answer is no, that's not possible in C# today.
We get a feature request like this fairly often; it's a nice feature in its more general form. The more general form is to more clearly make the lifetime of a local variable orthogonal to its scope.
Just to make sure those terms are clear: a variable is a storage location, possibly named. Every variable has a lifetime: the amount of time at runtime in which the variable is guaranteed to refer to valid storage. The scope of a name is the region of text in which that name may be used; it is a compile-time concept, not a runtime concept. A local variable is a variable whose scope is a statement block.
In many languages, the lifetime of a local variable is closely tied to its scope: when control logically enters the scope at runtime, the lifetime begins and when it leaves the scope, the lifetime ends. This is true in C# with some notable caveats:
The lifetime of a local may be extended or truncated if the runtime can determine that doing so has no consequence to the action of managed code on the current thread. The actions of other threads (like the finalizer thread) and unmanaged code on the current thread are implementation-defined.
The lifetime of a local that is in an iterator block, an async method, or a closed-over outer variable of an anonymous function, may be extended to match or exceed the lifetime of the iterator, task, delegate or expression tree that uses it.
Clearly it is not a requirement that the lifetime and scope of a local be tied together in any way. It would be nice if we could explicitly have locals that have the lifetime of an instance or static field, but the scope of a local. C has this feature; you can make a "static" local variable. C# does not. Your proposal is essentially to allow a local variable within the block of the property that has the lifetime of the instance but whose scope is restricted to the block.
I would classify this feature as "nice". We have a list of potential "nice" features literally as long as your arm that we don't have time to implement, so I wouldn't expect this one to make it to the top of the list any time soon. Thanks for the feedback though; it helps us prioritize that list somewhat.
Here's my take on that:
public class WrappedField<T>
{
public class Internals
{
public T Value;
}
private readonly Internals _internals = new Internals();
private readonly Func<Internals, T> _get;
private readonly Action<Internals, T> _set;
public T Value
{
get { return _get(_internals); }
set { _set(_internals, value); }
}
public WrappedField(Func<Internals, T> get, Action<Internals, T> set)
{
_get = get;
_set = set;
}
public WrappedField(Func<Internals, T> get, Action<Internals, T> set, T initialValue)
: this(get, set)
{
_set(_internals, initialValue);
}
}
Usage:
class Program
{
readonly WrappedField<int> _weight = new WrappedField<int>(
i => i.Value, // get
(i, v) => i.Value = v, // set
11); // initialValue
static void Main(string[] args)
{
Program p = new Program();
p._weight.Value = 10;
Console.WriteLine(p._weight.Value);
}
}
According to the C# 4.0 language specifications.
However, unlike fields, properties do not denote storage locations.
Instead, properties have accessors that specify the statements to be
executed when their values are read or written.
Adding a field would require a memory location. So no, this is not possible.
If you would like to avoid generics, you could always hide the _backingField and the bounds checking in a private inner class. You could even hide it further by making the outer class partial. Of course, there would have to be some delegating going on between the outer and the inner class, which is a bummer. Code to explain my thoughts:
public partial class MyClass
{
public int Property
{
get { return _properties.Property; }
set { _properties.Property = value; }
}
public void Stuff()
{
// Can't get to _backingField...
}
}
public partial class MyClass
{
private readonly Properties _properties = new Properties();
private class Properties
{
private int _backingField;
public int Property
{
get { return _backingField; }
set
{
// perform checks
_backingField = value;
}
}
}
}
But this is a lot of code. To justify all that boiler plate, the original problem has to be quite severe...
Nope, the only thing that can be within the the body of the property is the get and set.
Well, it's rather difficult to deal with, probably not very performant, and not something I'd really use, but technically it's a way of obscuring the backing field from the rest of the class.
public class MySuperAwesomeProperty<T>
{
private T backingField;
private Func<T, T> getter;
private Func<T, T> setter;
public MySuperAwesomeProperty(Func<T, T> getter, Func<T, T> setter)
{
this.getter = getter;
this.setter = setter;
}
public T Value
{
get
{
return getter(backingField);
}
set
{
backingField = setter(value);
}
}
}
public class Foo
{
public MySuperAwesomeProperty<int> Bar { get; private set; }
public Foo()
{
Bar = new MySuperAwesomeProperty<int>(
value => value, value => { doStuff(); return value; });
Bar.Value = 5;
Console.WriteLine(Bar.Value);
}
private void doStuff()
{
throw new NotImplementedException();
}
}
Related
I have the following object (class).
namespace Temp.Models
{
public class CurrentClass
{
private double _firstCoefficient;
private double _secondCoefficient;
public double FirstCoefficient
{
get { return _firstCoefficient; }
set { _firstCoefficient= value; }
}
public double SecondCoefficient
{
get { return _secondCoefficient; }
set { _secondCoefficient= value; }
}
}
}
The following class utilizes the above object and therefore initializes the object as follows:
namespace Temp.Models
{
public class MainClass
{
private CurrentClass _currentClass = new CurrentClass();
public CurrentClass CurrentClass
{
get { return _currentClass; }
set { _currentClass = value; }
}
}
}
At some point if certain conditions are met I would define the variables as follows:
MainClass currentObject = new MainClass();
//if conditions are met
currentObject.CurrentClass.FirstCoefficient = 0;
currentObject.CurrentClass.SecondCoefficient = 5;
But what if the conditions are never met and I never define the above variables. How and/or what is the best way to check if the object was never defined?
I can do the following check:
if(currentObject.CurrentClass.FirstCoefficient != 0 && currentObject.CurrentClass.SecondCoefficent != 0)
But the values can be defined as 0...So I am not sure how to go about this.
Any help is much appreciated!
These are some principles that can be used for solving the problem with description, samples and brief evaluation/opinion.
1. Parametrization through constructors
According to OOP principles, a constructor is method used to initialize an object to a valid state. The concept of immutability takes this even further, disallowing any changes, completely avoiding invalid state.
There is also a possibility of compromise where the API of an object disallows invalid states.
With this concept, you would arrive to:
namespace Temp.Models
{
public class CurrentClass
{
public double FirstCoefficient { get; private set; }
public double SecondCoefficient { get; private set; }
public CurrentClass(double firstCoefficient, double secondCoefficient)
{
FirstCoefficient = firstCoefficient;
SecondCoefficient = secondCoefficient;
}
// if mutability is required - this is needless as the constructor is
// the same but if there was more complex state, methods like this would make
// sense, mutating only parts of the state
public void SetCoefficients(double firstCoefficient, double secondCoefficient)
{
FirstCoefficient = firstCoefficient;
SecondCoefficient = secondCoefficient;
}
}
}
Summary:
Each instantiation of CurrentClass is always in a valid state, avoiding a lot of consistency checks (improved encapsulation)
It takes more code to write (but you save a lot of other code due to the previous point)
You need to know the coefficients beforehand.
2. Using nullable types
Nullable types add the "additional" value to types, the "undefined" state. Reference types (class) are nullable by design while value types (struct) need to be marked nullable, either as Nullable<T> or with the shorthand T?.
This then allows the objects be in invalid state and be specific about it. This goes to the other end of consistency scale from immutability as an object with multiple nullable fields has many invalid states.
Sample code:
namespace Temp.Models
{
public class CurrentClass
{
public double? FirstCoefficient { get; set; }
public double? SecondCoefficient { get; set; }
}
}
Now this gets instantiated quite nicely and can be changed on the fly:
public CurrentClass CreateCurrentClass()
{
var currentClass = new CurrentClass { FirstCoefficient = 1.0 };
var secondCoefficient = RetrieveSecondCoefficient();
currentClass.SecondCoefficient = secondCoefficient;
return currentClass;
}
You'll however need validity checks everywhere the object is used.
public bool IsValid(CurrentClass currentClass)
{
// what if FirstCoefficient has value and SecondCoefficient doesn't,
// is that always an invalid state?
return currentClass.FirstCoefficient.HasValue
&& currentClass.SecondCoefficient.HasValue;
}
Summary:
Very little code is needed to have a DTO up and running
A lot of consistency checks (and related brain pain) are required to work with such model
Encapsulation is lacking - any method taking CurrentClass can alter its validity, therefore making the previous point even worse. This can be eased by usage of read-only interface passed where read-only access is required.
Summing up
There are many other means that usually lay in between the two aforementioned approaches. For example you can use one validity flag (SergeyS's response) per object and ease on the external validity checks but having more code in the class and the need of deeper thinking.
Personally, I prefer immutability. It's more monkey code to write but will definitely pay off down the road thanks to the clean design.
A complex system without immutability is very hard to reason about without extensive knowledge. This is especially painful when working in a team - usually each person only knows a part of the codebase.
The sad thing is that it's not always possible to have evertything immutable (e.g. viewmodels): then I tend to convert objects to an internal immutable model as soon as it's possible.
Given what you already wrote, I would add Initialize() method and Initialized property into your MainClass class. Something similar to this:
public class MainClass
{
private CurrentClass _currentClass = new CurrentClass();
public CurrentClass CurrentClass
{
get { return _currentClass; }
set { _currentClass = value; }
}
public bool Initialized {get; private set;}
public void Initialize()
{
this.CurrentClass.FirstCoefficient = 0;
this.CurrentClass.SecondCoefficient = 5;
this.Initialized = true;
}
}
Call Initialize() method where your conditions met.
Later in code you can just check if(currentObject.Initialized). Notice private setter for `Initialized' property, it will ensure this flag was not accidentally set by external code.
Depending on your needs, you can go further and pass parameters for initialization directly to Initialize() method as parameters.
You have several approaches, like force values to be correct in constructor or have another variable telling if object has no value yet, like System.Drawing.Point has static "Empty" property. But in this case of your simple object your main class is explicitly creating an instance of CurrentClass so at this point this object should be correct and coefficients should be set. If you rely on some other code to set those values to perform some other action later, it is out of scope of these two objects here.
Update: perharps sharing details of what the real problem is would be better, because I have a feeling trying to provide a simpified example ended up in hiding real problem.
I want to store some backing fields of Properties declared in derived classes in protected Hashtable contained in base class.
The usage of this mechanism in derived classes has to beas simple as possible.
So, can I use MethodBase.GetCurrentMethod() to provide information about calling property (getter - properties are read-only), so it can be recognized as the one and only property that has access to this particular backing field?
EDIT:
Basically, I want to implement pattern:
private SomeClass _someProperty = null;
private SomeClass SomeProperty
{
if (_someProperty == null)
{
_someProperty = new SomeClass();
}
return _someProperty;
}
to look something like this:
private SomeClass SomeProperty
{
return GetProperty(delegate
{
var someProperty = new SomeClass();
return someProperty;
};
}
And in base class
private System.Collections.Hashtable _propertyFields = new System.Collections.Hashtable();
protected T GetProperty<T>(ConstructorDelegate<T> constructorBody)
{
var method = new System.Diagnostics.StackFrame(1).GetMethod();
if (!_propertyFields.ContainsKey(method))
{
var propertyObject = constructorBody.Invoke();
_propertyFields.Add(method, propertyObject);
}
return (T)_propertyFields[method];
}
protected delegate T ConstructorDelegate<T>();
The reason I want to do this is to simplify the usage of properties.
I use private properties to create some objects and use them around the class. But when I store their backing fields in the same class, I have the same access to them as to the properties, so I (means user who would create some derived classes in the future) could accidently use backing field instead of the property, so I wanted to restrict access to backing field, while allow to create object and use it.
I tried to use ObsoleteAttribute on the backing fields like this:
[Obsolete("Don't use this field. Please use corresponding property instead.")]
private SomeClass __someProperty;
private SomeClass _someProperty
{
#pragma warning disable 0618 //Disable Obsolete warning for property usage.
get
{
if (__someProperty== null)
{
__someProperty = new SomeClass();
}
return __someProperty ;
}
#pragma warning restore 0618 //Restore Obsolete warning for rest of the code.
}
But, firstly, I cannot force the user to use this pattern, and secondly, it's to much code to write in derived class, which, as I metioned above, I want to be as simple as possible.
Neither MethodBase nor MemberInfo do not properly overrides Equals and GetHashCode functions, but uses default RuntimeHelpers.GetHashCode and RuntimeHelpers.Equals. So you will only be able to compare same instance, but not same content. In most cases this will be enough as runtime caches that instances to reuse them. But there is no guarantee this will work stable.
As you working with metadata, use something that will identify it uniquely. For example, MemberInfo.MetadataToken. You could write your own comparer and use it inside hashtable:
public class MethodBaseComparer : IEqualityComparer<MethodBase>
{
public bool Equals(MethodBase x, MethodBase y)
{
if (ReferenceEquals(x, y))
return true;
if (ReferenceEquals(x, null) || ReferenceEquals(y, null))
return false;
return x.MetadataToken.Equals(y.MetadataToken) &&
x.MethodHandle.Equals(y.MethodHandle);
}
public int GetHashCode(MethodBase obj)
{
return (obj.MetadataToken.GetHashCode() * 387) ^ obj.MethodHandle.GetHashCode();
}
}
It not a good idea to restrict access via reflection to some members as other trusted code can use reflection to access other private data outflanking your checks. Consider restrict access via redesigning your classes.
Also take a look at Code Access Security.
Update according to your edit.
You told your properties are read-only. I guess, simply declaring them as readonly is not your option. Looks like you want delayed initialization for properties values. In that case you will not able to declare them as readonly. Right?
Or maybe you can?
Take a look at Lazy<T> class. It's not available in dotnet 2.0, but you can easily implement it or even take any existing implementation (just replace Func<T> with your delegate). Example usage:
public class Foo
{
private readonly Lazy<int> _bar = new Lazy<int>(() => Environment.TickCount, true);
// similar to your constructorBody - ^^^^^^^^^^^^^^^^^^^^^^^^^^^
private int Bar
{
get { return this._bar.Value; }
}
public void DoSomethingWithBar(string title)
{
Console.WriteLine("cur: {0}, foo.bar: {1} <- {2}",
Environment.TickCount,
this.Bar,
title);
}
}
Pros:
It's a lazy initialization as you wish. Let's test it:
public static void Main()
{
var foo = new Foo();
Console.WriteLine("cur: {0}", Environment.TickCount);
Thread.Sleep(300);
foo.DoSomethingWithBar("initialization");
Thread.Sleep(300);
foo.DoSomethingWithBar("later usage");
}
Output will be something like this:
cur: 433294875
cur: 433295171, foo.bar: 433295171 <- initialization
cur: 433295468, foo.bar: 433295171 <- later usage
Note, value initialized on first access and not changed later.
Properties are write-protected by a compiler - _bar field is readonly and you have no access to internal fields of Lazy<T>. So, no any accidental backing field usage. If you try you will get compilation error on type mismatch:
CS0029 Cannot implicitly convert type System.Lazy<SomeClass> to SomeClass
And even if you access it via this._bar.Value, nothing terrible would happen and you will get a correct value as if you access it via this.Bar property.
It is much more simpler, faster and easier to read and maintain.
Thread safety out of the box.
Cons: — (I didn't found)
Few cents about your hashtable-based design:
You (or someone who will maintain your code) can accidentally (or advisedly) access and/or modify either whole hashtable or it's items as it is just a usual private property.
Hashtable is a minor performance hit + getting stacktrace is a huge performance hit. However I don't know if it is critical, depends on how often you access your properties.
It would be hard to read and maintain.
Not thread safe.
I have some delegates, two classes and a struct that look kind of like this:
delegate Value Combination(Value A, Value B);
class Environment
{
private Combination[][] combinations;
private string[] typenames;
private getString[] tostrings;
public Environment() { ... } //adds one 'Null' type at index 0 by default
public void AddType(string name, getString tostring, Combination[] combos) { ... }
public Value Combine(Value A, Value B)
{
return combinations[A.index][B.index](A, B);
}
public string getStringValue(Value A)
{
return tostrings[A.index](A);
}
public string getTypeString(Value A)
{
return typenames[A.index];
}
}
class Container
{
public string contents
{
get
{
return data.text;
}
}
public string contentType
{
get
{
return data.type;
}
}
private Value data;
public Container(Value val)
{
data = val;
}
public Container CombineContents(Container B)
{
return new Container(data.Combine(B.data))
}
}
struct Value
{
public string type
{
get
{
return environment.getTypeString(this);
}
}
public string text
{
get
{
return environment.getStringValue(this);
}
}
public readonly int type;
public readonly byte[] raw;
public readonly Environment environment;
public Value(int t, byte[] bin, Environment env)
{
type = t;
raw = bin;
environment = env;
}
public Value Combine(Value B)
{
return environment.Combine(this, B)
}
}
The reason for this structure is that Containers can have Values of various types, which combine with each other in user-defined ways according to the current Environment (which, like Container and Value, is differently named so as to avoid conflicting with the System.Environment class in my actual code- I used the name here to concisely imply its function). I cannot get around the problem with subclasses of Value and generic Containers since values of different types still need to be combinable, and neither Container nor the base Value class can know what type of Value combination should return.
It doesn't seem possible to define the Environment class in a global way, as the existing System.Environment class doesn't seem to allow storing delegates as user variables, and giving it a static method returning an instance of itself would render it unmodifiable*, and would require a new instance of the class to be created every time I want to do anything with Values, which seems like it should be a huge performance hit.
This causes two problems for me:
There is an extra reference padding out all my Values. Values are variable in size, but raw is almost always 8 bits or less, so the difference is significant, especially since in actual implementations it will be fairly common to have several million Values and Containers in memory at once.
It is impossible to define a proper 'null' Value, as a Value must have an Environment in it and the Environment must be mutable. This in turn means that Container constructors that do not take a Value as an argument are much more convoluted.
The only other way around this I can think of would be to have a wrapper class (either an extension of Environment or something with an environment as a parameter) which is required in order to work with Containers or Values, which has all extant Containers and Values as members. This would solve the 'null' problem and neaten up the Value class a bit, but adds a huge amount of overhead as described and makes for a really convoluted interface for the end user. Those problems are, with a good deal of work and some changes in program flow, solvable as well, but by that point I'm pretty much writing another programming language which is far more than I should need.
Is there any other workaround for this that I'm missing, or am I mistaken about any of my disqualifying factors above? The only thing I can think of is that the performance hit from the static implementation might be smaller than I think it would be due to cacheing (I cannot perform realistic benchmarking unfortunately- there are too many variables in how this could be used).
*Note that an environment doesn't strictly speaking need to be modifiable- there would be no problem, technically, for example, with something like
class Environment
{
private Combination[][] combinations;
private string[] typenames;
private getString[] tostrings;
public Environment(Combination[][] combos, string[] tnames, getString[] getstrings)
{
combinations = combos;
typenames = tnames;
tostrings = getstrings;
}
}
except that this would be much more awkward for the end user, and doesn't actually fix any of the problems I've noted above.
I had a lot of trouble trying to understand exactly what you were trying to achieve here! So apologies if I'm off the mark. Here is a singleton based example that, if I understand the problem correctly, may help you:
public class CombinationDefinition
{
public string Name;
public getString GetString;
public Combination[] Combinations;
}
public static class CurrentEnvironment
{
public static CombinationDefinition[] Combinations = new CombinationDefinition[0];
public static Environment Instance { get { return _instance.Value; } }
static ThreadLocal<Environment> _instance = new ThreadLocal<Environment>(() =>
{
Environment environment = new Environment();
foreach (var combination in Combinations)
environment.AddType(combination.Name, combination.GetString, combination.Combinations);
return environment;
});
public static Value CreateValue(int t, byte[] bin)
{
return new Value(t, bin, Instance);
}
}
Which can be used as:
CurrentEnvironment.Combinations = new CombinationDefinition[]
{
new CombinationDefinition() { Name = "Combination1", GetString = null, Combinations = null },
new CombinationDefinition() { Name = "Combination2", GetString = null, Combinations = null },
};
Value value = CurrentEnvironment.CreateValue(123, null);
string stringValue = CurrentEnvironment.Instance.getStringValue(value);
Important to note - CurrentEnvironment.Combinations must be set before the Environment is used for the first time as accessing the Instance property for the first time will cause the Environment to be instantiated by its ThreadLocal container. This instantiation uses the values in Combinationsto use the existing AddType method to populate the Environment.
You either need to make Environment a "Singleton" (recomended), or mark everything inside it as static. Another possibility is to use an IoC container, but that may be more advanced than you are ready to go for at this point.
The Singleton pattern usually declared a static Instance property that is initialized to a new instance of the class through a private constructor. All access is done through the static Instance property, which will be available globally. You can read more about Singletons in C# here.
static will allow you to access the members without instantiating an instance of the class and it will act as a "global" container.
Singleton Example:
class Environment
{
private static Environment _instance;
public static Environment Instance
{
get
{
if (_instance == null)
{
_instance = new Environment();
}
return _instance;
}
}
private Environment(){}
private Combination[][] combinations;
private string[] typenames;
private getString[] tostrings;
public Environment() { ... } //adds one 'Null' type at index 0 by default
public void AddType(string name, getString tostring, Combination[] combos) { ... }
public Value Combine(Value A, Value B)
{
return combinations[A.index][B.index](A, B);
}
public string getStringValue(Value A)
{
return tostrings[A.index](A);
}
public string getTypeString(Value A)
{
return typenames[A.index];
}
}
Example usage:
Environment.Instance.getStringValue(this);
Please excuse any syntax errors in code, I don't have access to Visual Studio at the moment.
The C# "readonly" keyword is a modifier that when a field declaration includes it, assignments to the fields introduced by the declaration can only occur as part of the declaration or in a constructor in the same class.
Now suppose I do want this "assign value once" constraint, but I would rather allow the assignment be done outside of constructors, a lazy/late evaluation/initialization maybe.
How could I do that? and is it possible to do it in a nice way, for example, is it possible to write some attribute to describe this?
If I understand your question correctly, it sounds like you just want to set a field's value once (the first time), and not allow it to be set after that. If that is so, then all the previous posts about using Lazy (and related) may be useful. But if you don't want to use those suggestions, perhaps you can do something like this:
public class SetOnce<T>
{
private T mySetOnceField;
private bool isSet;
// used to determine if the value for
// this SetOnce object has already been set.
public bool IsSet
{
get { return isSet; }
}
// return true if this is the initial set,
// return false if this is after the initial set.
// alternatively, you could make it be a void method
// which would throw an exception upon any invocation after the first.
public bool SetValue(T value)
{
// or you can make thread-safe with a lock..
if (IsSet)
{
return false; // or throw exception.
}
else
{
mySetOnceField = value;
return isSet = true;
}
}
public T GetValue()
{
// returns default value of T if not set.
// Or, check if not IsSet, throw exception.
return mySetOnceField;
}
} // end SetOnce
public class MyClass
{
private SetOnce<int> myReadonlyField = new SetOnce<int>();
public void DoSomething(int number)
{
// say this is where u want to FIRST set ur 'field'...
// u could check if it's been set before by it's return value (or catching the exception).
if (myReadOnlyField.SetValue(number))
{
// we just now initialized it for the first time...
// u could use the value: int myNumber = myReadOnlyField.GetValue();
}
else
{
// field has already been set before...
}
} // end DoSomething
} // end MyClass
Now suppose I do want this "assign value once" constraint, but I would rather allow the assignment be done outside of constructors
Note that lazy initialization is complicated, so for all of these answers you should be careful if you have multiple threads trying to access your object.
If you want to do this inside the class
You can use the C# 4.0 built-in lazy initialization features:
http://msdn.microsoft.com/en-us/library/dd997286.aspx
http://msdn.microsoft.com/en-us/library/dd642331.aspx
http://sankarsan.wordpress.com/2009/10/04/laziness-in-c-4-0-lazyt/
Or for older versions of C#, just supply a get method, and check if you're already initialized by using a backing field:
public string SomeValue
{
get
{
// Note: Not thread safe...
if(someValue == null)
{
someValue = InitializeSomeValue(); // Todo: Implement
}
return someValue;
}
}
If you want to do this outside the class
You want Popsicle Immutability:
http://blogs.msdn.com/b/ericlippert/archive/2007/11/13/immutability-in-c-part-one-kinds-of-immutability.aspx
http://msdn.microsoft.com/en-us/library/ms750509.aspx
http://csharpindepth.com/Talks.aspx (search for "popsicle immutability" and you'll find a video)
Basically:
You make the whole class writable, but add a Freeze method.
Once this freeze method is called, if users try to call setters or mutator methods on your class, you throw a ModifyFrozenObjectException.
You probably want a way for external classes to determine if your class IsFrozen.
BTW, I made up these names just now. My selections are admittedly poor, but there is no generically followed convention for this yet.
For now I'd recommend you create an IFreezable interface, and possibly related exceptions, so you don't have to depend on the WPF implementation. Something like:
public interface IFreezable
{
void Freeze();
bool IsFrozen { get; }
}
You can use the Lazy<T> class:
private readonly Lazy<Foo> _foo = new Lazy<Foo>(GetFoo);
public Foo Foo
{
get { return _foo.Value; }
}
private static Foo GetFoo()
{
// somehow create a Foo...
}
GetFoo will only be called the first time you call the Foo property.
This is know as the "once" feature in Eiffel. It is a major oversight in C#. The new Lazy type is a poor substitute since it is not interchangeable with its non-lazy version but instead requires you to access the contained value through its Value property. Consequently, I rarely use it. Noise is one of the biggest problems with C# code. Ideally, one wants something like this...
public once Type PropertyName { get { /* generate and return value */ } }
as oppose to the current best practice...
Type _PropertyName; //where type is a class or nullable structure
public Type PropertyName
{
get
{
if (_PropertyName == null)
_PropertyName = /* generate and return value */
return _PropertyName
}
}
In the following code:
public class MovieRepository : IMovieRepository
{
private readonly IHtmlDownloader _downloader;
public MovieRepository(IHtmlDownloader downloader)
{
_downloader = downloader;
}
public Movie FindMovieById(string id)
{
var idUri = ...build URI...;
var html = _downloader.DownloadHtml(idUri);
return ...parse ID HTML...;
}
public Movie FindMovieByTitle(string title)
{
var titleUri = ...build URI...;
var html = _downloader.DownloadHtml(titleUri);
return ...parse title HTML...;
}
}
I asked for something to review my code, and someone suggested this approach. My question is why is the IHtmlDownloader variable readonly?
If it's private and readonly, the benefit is that you can't inadvertently change it from another part of that class after it is initialized. The readonly modifier ensures the field can only be given a value during its initialization or in its class constructor.
If something functionally should not change after initialization, it's always good practice to use available language constructs to enforce that.
On a related note, C# 9 introduces the init accessor method for properties, which indicates the property value can only be set during object construction, e.g.:
class InitExample
{
private double _seconds;
public double Seconds
{
get { return _seconds; }
init { _seconds = value; }
}
}
This ensures that the value of _downloader will not be changed after the constructor was executed. Fields marked as readonly can only be assigned a value from within the constructor(s) of a class.
A readonly field is useful for modelling data that should not change after it has been initialized. You can assign a value to a readonly field by using a initializer when you declare it or in a constructor, but thereafter you cannot change it.