Related
I am looking at some legacy code and have come across an abstraction that has properties for each of its derived/concrete types. I cannot share the exact code but please imagine that instead of it being a simple operation that there are numerous operations that are much more complex.
I have not come across anything like this before and have a lot of questions? First, is this a pattern that I am not aware of? If so, what is it? Second question, how should I refactor this so that it follows solid principles?
I will try my best to come up with a better example if needed.
public enum ToolType
{
Unknown = 0,
HRMonitor,
Dumbell,
SomeForceDevice
}
public abstract class ToolData
{
private ToolData()
{
IsValid = false;
this.ToolType = ToolType.Unknown;
}
public ToolData(ToolType toolType)
{
this.ToolType = toolType;
}
public ToolType ToolType { get; }
public virtual bool IsValid { get; protected set; } = true;
public double LinkQuality { get; set; }
public NullToolDataValue NullData => this as NullToolDataValue;
public DumbellDataValue DumbellData => this as DumbellDataValue;
public HeartRateDataValue HRData => this as HeartRateDataValue;
public SomeForceDataValue SomeForceData => this as SomeForceDataValue;
}
public class NullToolDataValue : ToolData
{
public NullToolDataValue() : base(ToolType.Unknown)
{
IsValid = false;
}
}
public class DumbellDataValue : ToolData
{
public double WeightValue { get; private set; }
public DumbellDataValue(double weightValue) : base(ToolType.Dumbell)
{
this.WeightValue = weightValue;
}
public override string ToString()
{
return WeightValue.ToString(CultureInfo.InvariantCulture);
}
}
public class HeartRateDataValue : ToolData
{
public int HeartRate { get; private set; }
public HeartRateDataValue(int heartRate) : base(ToolType.HRMonitor)
{
this.HeartRate = heartRate;
}
public override string ToString()
{
return HeartRate.ToString(CultureInfo.InvariantCulture);
}
}
public class SomeForceDataValue : ToolData
{
public double LeftHandForceValue { get; private set; }
public double RightHandForceValue { get; private set; }
public int LeftHandPosition { get; private set; }
public int RightHandPosition { get; private set; }
public SomeForceDataValue(double lefthandValue, double rightHandValue, int leftHandPosition, int rightHandPosition) : base(ToolType.SomeForceDevice)
{
this.LeftHandForceValue = lefthandValue;
this.LeftHandPosition = leftHandPosition;
this.RightHandForceValue = rightHandValue;
this.RightHandPosition = rightHandPosition;
}
public override string ToString()
{
return $"{LeftHandForceValue.ToString(CultureInfo.InvariantCulture)}" +
$"| {LeftHandPosition.ToString(CultureInfo.InvariantCulture)}" +
$"| {RightHandForceValue.ToString(CultureInfo.InvariantCulture)}" +
$"| {RightHandPosition.ToString(CultureInfo.InvariantCulture)}";
}
}
It is being used/consumed via something like the below which it too is missing some inheritance and things for brevity:
public class DumbellExcercise
{
public void ToolDataReceived(ToolData data)
{
if (data?.DumbellData == null) return;
//add value to some collection
Collection.Add(data.DumbellData.WeightValue);
}
}
public class HRExcercise
{
public void ToolDataReceived(ToolData data)
{
if (data?.HRData == null) return;
//add value to some collection
Collection.Add(data.HRData.HeartRate);
}
}
Okay, I'm going to give a shot at answering - hopefully this will help.
First up, ToolData shouldn't contain any References/Enums/whatever that list its subtypes. So first on the chopping block: all the lambda properties that cast the object as a specific subtype. I can kinda understand the appeal - you know an instance of ToolType happens to be a FloobieTool, so you call instance.FloobieTool and magically get a FloobieTool cast. But... well, there are problems that come with it, not the least is that you're breaking Opened/Closed Principle. Nothing wrong with making the person calling the class cast it explicitly with (FloobieTool)instance if they know they're working with a FloobieTool.
Next up: the ToolType. Why do you need this? You can tell if your instance of ToolData is a FloobieTool by simply doing an 'is' check in an IF condition:
void SomeFunc(ToolData toolData)
{
if (!(toolData is FloobieTool)) throw new Exception("Non-Floobie!");
// more code
}
I mean, what does that enumeration actually get you? Because it has a definite cost: it has to be kept in-sync with the list of classes that implement ToolData.
Also, the part in ToolDataReceived() for each of those Exercise classes seems... weird. I mean, you've got an exercise, and you're passing in ToolData. Why are you storing the amount of the Dumbell exercise? As opposed to just storing the ToolData. I mean, you're going through quite a bit of testing/casting/etc, just to add the dumbbell weight to a Collection. Any reason you can't just store the ToolData instance and call it a day? If you really need to specifically store Dumbbell info, you could do something like:
public class DumbbellExercise
{
List<DumbbellDataValue> dumbbellData = new List<DumbbellDataValue>();
public void AddToolData(ToolData toolData)
{
if (toolData is DumbbellDataValue)
this.dumbbellData.Add((DumbbellDataValue)toolData);
}
}
Hopefully that helps - it's tough to go into too many details when we're working off an abstracted example of your actual problem :-)
Having seen your edits, I believe even more firmly that the way to refactor this code is to use pattern matching. Pattern matching requires at least C# 7.0 so I'll include an almost-as-good way to do it pre-7.0 versions.
Step 1
Mark the properties obsolete using ObsoleteAttribute and pass true for the error parameter.
[Obsolete("Use pattern matching instead.", true)]
public NullToolDataValue NullData => this as NullToolDataValue;
[Obsolete("Use pattern matching instead.", true)]
public DumbellDataValue DumbellData => this as DumbellDataValue;
[Obsolete("Use pattern matching instead.", true)]
public HeartRateDataValue HRData => this as HeartRateDataValue;
[Obsolete("Use pattern matching instead.", true)]
public SomeForceDataValue SomeForceData => this as SomeForceDataValue;
This will make it a compiler error to use them in any code processed by the compiler. If you're doing any reflection on them, you'll get a runtime exception instead (after step 3 is complete) if you don't also change that code.
Step 2
Modify every call site that uses those properties to use pattern matching instead. If all you're doing is what you showed in the question, is should be as simple as this:
public class DumbellExcercise
{
public void ToolDataReceived(ToolData data)
{
if (data is DumbellDataValue dumbell)
Collection.Add(dumbell.WeightValue);
// OR
if (!(data is DumbellDataValue dumbell))
return;
Collection.Add(dumbell.WeightValue);
}
}
The second variation is not as pretty because the condition has to be parenthesized before it can be negated (hey, at least VB has the IsNot keyword; go figure) but you get the same early return that the existing code has.
It looks like you're using at least C# 6.0 because you're using the null-coalescing operator (?.), but if you're not using at least 7.0, you can do this, instead:
public class DumbellExcercise
{
public void ToolDataReceived(ToolData data)
{
DumbellDataValue dumbell = data as DumbellDataValue;
if (dumbell != null)
Collection.Add(dumbell.WeightValue);
// OR
DumbellDataValue dumbell = data as DumbellDataValue;
if (dumbell == null)
return;
Collection.Add(dumbell.WeightValue);
}
}
Step 3
Remove the properties. If there are no more compiler errors, the properties aren't being used, so you're free to get rid of them.
Additional Note
The IsValid property has a strange duality to it. It can be assigned by the derived classes but it's also virtual so it can be overridden, too. You really should pick one. If it were my decision, I'd keep it virtual and make it read-only.
public abstract class ToolData
{
// Continue to assume it's true...
public virtual bool IsValid => true;
}
public class NullToolDataValue : ToolData
{
// ...and indicate otherwise as needed.
public override bool IsValid => false;
}
How do you give a C# auto-property an initial value?
I either use the constructor, or revert to the old syntax.
Using the Constructor:
class Person
{
public Person()
{
Name = "Initial Name";
}
public string Name { get; set; }
}
Using normal property syntax (with an initial value)
private string name = "Initial Name";
public string Name
{
get
{
return name;
}
set
{
name = value;
}
}
Is there a better way?
In C# 5 and earlier, to give auto implemented properties an initial value, you have to do it in a constructor.
Since C# 6.0, you can specify initial value in-line. The syntax is:
public int X { get; set; } = x; // C# 6 or higher
DefaultValueAttribute is intended to be used by the VS designer (or any other consumer) to specify a default value, not an initial value. (Even if in designed object, initial value is the default value).
At compile time DefaultValueAttribute will not impact the generated IL and it will not be read to initialize the property to that value (see DefaultValue attribute is not working with my Auto Property).
Example of attributes that impact the IL are ThreadStaticAttribute, CallerMemberNameAttribute, ...
Edited on 1/2/15
C# 6 :
With C# 6 you can initialize auto-properties directly (finally!), there are now other answers that describe that.
C# 5 and below:
Though the intended use of the attribute is not to actually set the values of the properties, you can use reflection to always set them anyway...
public class DefaultValuesTest
{
public DefaultValuesTest()
{
foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(this))
{
DefaultValueAttribute myAttribute = (DefaultValueAttribute)property.Attributes[typeof(DefaultValueAttribute)];
if (myAttribute != null)
{
property.SetValue(this, myAttribute.Value);
}
}
}
public void DoTest()
{
var db = DefaultValueBool;
var ds = DefaultValueString;
var di = DefaultValueInt;
}
[System.ComponentModel.DefaultValue(true)]
public bool DefaultValueBool { get; set; }
[System.ComponentModel.DefaultValue("Good")]
public string DefaultValueString { get; set; }
[System.ComponentModel.DefaultValue(27)]
public int DefaultValueInt { get; set; }
}
When you inline an initial value for a variable it will be done implicitly in the constructor anyway.
I would argue that this syntax was best practice in C# up to 5:
class Person
{
public Person()
{
//do anything before variable assignment
//assign initial values
Name = "Default Name";
//do anything after variable assignment
}
public string Name { get; set; }
}
As this gives you clear control of the order values are assigned.
As of C#6 there is a new way:
public string Name { get; set; } = "Default Name";
Sometimes I use this, if I don't want it to be actually set and persisted in my db:
class Person
{
private string _name;
public string Name
{
get
{
return string.IsNullOrEmpty(_name) ? "Default Name" : _name;
}
set { _name = value; }
}
}
Obviously if it's not a string then I might make the object nullable ( double?, int? ) and check if it's null, return a default, or return the value it's set to.
Then I can make a check in my repository to see if it's my default and not persist, or make a backdoor check in to see the true status of the backing value, before saving.
In C# 6.0 this is a breeze!
You can do it in the Class declaration itself, in the property declaration statements.
public class Coordinate
{
public int X { get; set; } = 34; // get or set auto-property with initializer
public int Y { get; } = 89; // read-only auto-property with initializer
public int Z { get; } // read-only auto-property with no initializer
// so it has to be initialized from constructor
public Coordinate() // .ctor()
{
Z = 42;
}
}
Starting with C# 6.0, We can assign default value to auto-implemented properties.
public string Name { get; set; } = "Some Name";
We can also create read-only auto implemented property like:
public string Name { get; } = "Some Name";
See: C# 6: First reactions , Initializers for automatically implemented properties - By Jon Skeet
In Version of C# (6.0) & greater, you can do :
For Readonly properties
public int ReadOnlyProp => 2;
For both Writable & Readable properties
public string PropTest { get; set; } = "test";
In current Version of C# (7.0), you can do : (The snippet rather displays how you can use expression bodied get/set accessors to make is more compact when using with backing fields)
private string label = "Default Value";
// Expression-bodied get / set accessors.
public string Label
{
get => label;
set => this.label = value;
}
In C# 9.0 was added support of init keyword - very useful and extremly sophisticated way for declaration read-only auto-properties:
Declare:
class Person
{
public string Name { get; init; } = "Anonymous user";
}
~Enjoy~ Use:
// 1. Person with default name
var anonymous = new Person();
Console.WriteLine($"Hello, {anonymous.Name}!");
// > Hello, Anonymous user!
// 2. Person with assigned value
var me = new Person { Name = "#codez0mb1e"};
Console.WriteLine($"Hello, {me.Name}!");
// > Hello, #codez0mb1e!
// 3. Attempt to re-assignment Name
me.Name = "My fake";
// > Compilation error: Init-only property can only be assigned in an object initializer
In addition to the answer already accepted, for the scenario when you want to define a default property as a function of other properties you can use expression body notation on C#6.0 (and higher) for even more elegant and concise constructs like:
public class Person{
public string FullName => $"{First} {Last}"; // expression body notation
public string First { get; set; } = "First";
public string Last { get; set; } = "Last";
}
You can use the above in the following fashion
var p = new Person();
p.FullName; // First Last
p.First = "Jon";
p.Last = "Snow";
p.FullName; // Jon Snow
In order to be able to use the above "=>" notation, the property must be read only, and you do not use the get accessor keyword.
Details on MSDN
In C# 6 and above you can simply use the syntax:
public object Foo { get; set; } = bar;
Note that to have a readonly property simply omit the set, as so:
public object Foo { get; } = bar;
You can also assign readonly auto-properties from the constructor.
Prior to this I responded as below.
I'd avoid adding a default to the constructor; leave that for dynamic assignments and avoid having two points at which the variable is assigned (i.e. the type default and in the constructor). Typically I'd simply write a normal property in such cases.
One other option is to do what ASP.Net does and define defaults via an attribute:
http://msdn.microsoft.com/en-us/library/system.componentmodel.defaultvalueattribute.aspx
My solution is to use a custom attribute that provides default value property initialization by constant or using property type initializer.
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class InstanceAttribute : Attribute
{
public bool IsConstructorCall { get; private set; }
public object[] Values { get; private set; }
public InstanceAttribute() : this(true) { }
public InstanceAttribute(object value) : this(false, value) { }
public InstanceAttribute(bool isConstructorCall, params object[] values)
{
IsConstructorCall = isConstructorCall;
Values = values ?? new object[0];
}
}
To use this attribute it's necessary to inherit a class from special base class-initializer or use a static helper method:
public abstract class DefaultValueInitializer
{
protected DefaultValueInitializer()
{
InitializeDefaultValues(this);
}
public static void InitializeDefaultValues(object obj)
{
var props = from prop in obj.GetType().GetProperties()
let attrs = prop.GetCustomAttributes(typeof(InstanceAttribute), false)
where attrs.Any()
select new { Property = prop, Attr = ((InstanceAttribute)attrs.First()) };
foreach (var pair in props)
{
object value = !pair.Attr.IsConstructorCall && pair.Attr.Values.Length > 0
? pair.Attr.Values[0]
: Activator.CreateInstance(pair.Property.PropertyType, pair.Attr.Values);
pair.Property.SetValue(obj, value, null);
}
}
}
Usage example:
public class Simple : DefaultValueInitializer
{
[Instance("StringValue")]
public string StringValue { get; set; }
[Instance]
public List<string> Items { get; set; }
[Instance(true, 3,4)]
public Point Point { get; set; }
}
public static void Main(string[] args)
{
var obj = new Simple
{
Items = {"Item1"}
};
Console.WriteLine(obj.Items[0]);
Console.WriteLine(obj.Point);
Console.WriteLine(obj.StringValue);
}
Output:
Item1
(X=3,Y=4)
StringValue
little complete sample:
using System.ComponentModel;
private bool bShowGroup ;
[Description("Show the group table"), Category("Sea"),DefaultValue(true)]
public bool ShowGroup
{
get { return bShowGroup; }
set { bShowGroup = value; }
}
You can simple put like this
public sealed class Employee
{
public int Id { get; set; } = 101;
}
In the constructor. The constructor's purpose is to initialized it's data members.
private string name;
public string Name
{
get
{
if(name == null)
{
name = "Default Name";
}
return name;
}
set
{
name = value;
}
}
Have you tried using the DefaultValueAttribute or ShouldSerialize and Reset methods in conjunction with the constructor? I feel like one of these two methods is necessary if you're making a class that might show up on the designer surface or in a property grid.
Use the constructor because "When the constructor is finished, Construction should be finished". properties are like states your classes hold, if you had to initialize a default state, you would do that in your constructor.
To clarify, yes, you need to set default values in the constructor for class derived objects. You will need to ensure the constructor exists with the proper access modifier for construction where used. If the object is not instantiated, e.g. it has no constructor (e.g. static methods) then the default value can be set by the field. The reasoning here is that the object itself will be created only once and you do not instantiate it.
#Darren Kopp - good answer, clean, and correct. And to reiterate, you CAN write constructors for Abstract methods. You just need to access them from the base class when writing the constructor:
Constructor at Base Class:
public BaseClassAbstract()
{
this.PropertyName = "Default Name";
}
Constructor at Derived / Concrete / Sub-Class:
public SubClass() : base() { }
The point here is that the instance variable drawn from the base class may bury your base field name. Setting the current instantiated object value using "this." will allow you to correctly form your object with respect to the current instance and required permission levels (access modifiers) where you are instantiating it.
public Class ClassName{
public int PropName{get;set;}
public ClassName{
PropName=0; //Default Value
}
}
This is old now, and my position has changed. I'm leaving the original answer for posterity only.
Personally, I don't see the point of making it a property at all if you're not going to do anything at all beyond the auto-property. Just leave it as a field. The encapsulation benefit for these item are just red herrings, because there's nothing behind them to encapsulate. If you ever need to change the underlying implementation you're still free to refactor them as properties without breaking any dependent code.
Hmm... maybe this will be the subject of it's own question later
class Person
{
/// Gets/sets a value indicating whether auto
/// save of review layer is enabled or not
[System.ComponentModel.DefaultValue(true)]
public bool AutoSaveReviewLayer { get; set; }
}
I know this is an old question, but it came up when I was looking for how to have a default value that gets inherited with the option to override, I came up with
//base class
public class Car
{
public virtual string FuelUnits
{
get { return "gasoline in gallons"; }
protected set { }
}
}
//derived
public class Tesla : Car
{
public override string FuelUnits => "ampere hour";
}
I think this would do it for ya givng SomeFlag a default of false.
private bool _SomeFlagSet = false;
public bool SomeFlag
{
get
{
if (!_SomeFlagSet)
SomeFlag = false;
return SomeFlag;
}
set
{
if (!_SomeFlagSet)
_SomeFlagSet = true;
SomeFlag = value;
}
}
How do you give a C# auto-property an initial value?
I either use the constructor, or revert to the old syntax.
Using the Constructor:
class Person
{
public Person()
{
Name = "Initial Name";
}
public string Name { get; set; }
}
Using normal property syntax (with an initial value)
private string name = "Initial Name";
public string Name
{
get
{
return name;
}
set
{
name = value;
}
}
Is there a better way?
In C# 5 and earlier, to give auto implemented properties an initial value, you have to do it in a constructor.
Since C# 6.0, you can specify initial value in-line. The syntax is:
public int X { get; set; } = x; // C# 6 or higher
DefaultValueAttribute is intended to be used by the VS designer (or any other consumer) to specify a default value, not an initial value. (Even if in designed object, initial value is the default value).
At compile time DefaultValueAttribute will not impact the generated IL and it will not be read to initialize the property to that value (see DefaultValue attribute is not working with my Auto Property).
Example of attributes that impact the IL are ThreadStaticAttribute, CallerMemberNameAttribute, ...
Edited on 1/2/15
C# 6 :
With C# 6 you can initialize auto-properties directly (finally!), there are now other answers that describe that.
C# 5 and below:
Though the intended use of the attribute is not to actually set the values of the properties, you can use reflection to always set them anyway...
public class DefaultValuesTest
{
public DefaultValuesTest()
{
foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(this))
{
DefaultValueAttribute myAttribute = (DefaultValueAttribute)property.Attributes[typeof(DefaultValueAttribute)];
if (myAttribute != null)
{
property.SetValue(this, myAttribute.Value);
}
}
}
public void DoTest()
{
var db = DefaultValueBool;
var ds = DefaultValueString;
var di = DefaultValueInt;
}
[System.ComponentModel.DefaultValue(true)]
public bool DefaultValueBool { get; set; }
[System.ComponentModel.DefaultValue("Good")]
public string DefaultValueString { get; set; }
[System.ComponentModel.DefaultValue(27)]
public int DefaultValueInt { get; set; }
}
When you inline an initial value for a variable it will be done implicitly in the constructor anyway.
I would argue that this syntax was best practice in C# up to 5:
class Person
{
public Person()
{
//do anything before variable assignment
//assign initial values
Name = "Default Name";
//do anything after variable assignment
}
public string Name { get; set; }
}
As this gives you clear control of the order values are assigned.
As of C#6 there is a new way:
public string Name { get; set; } = "Default Name";
Sometimes I use this, if I don't want it to be actually set and persisted in my db:
class Person
{
private string _name;
public string Name
{
get
{
return string.IsNullOrEmpty(_name) ? "Default Name" : _name;
}
set { _name = value; }
}
}
Obviously if it's not a string then I might make the object nullable ( double?, int? ) and check if it's null, return a default, or return the value it's set to.
Then I can make a check in my repository to see if it's my default and not persist, or make a backdoor check in to see the true status of the backing value, before saving.
In C# 6.0 this is a breeze!
You can do it in the Class declaration itself, in the property declaration statements.
public class Coordinate
{
public int X { get; set; } = 34; // get or set auto-property with initializer
public int Y { get; } = 89; // read-only auto-property with initializer
public int Z { get; } // read-only auto-property with no initializer
// so it has to be initialized from constructor
public Coordinate() // .ctor()
{
Z = 42;
}
}
Starting with C# 6.0, We can assign default value to auto-implemented properties.
public string Name { get; set; } = "Some Name";
We can also create read-only auto implemented property like:
public string Name { get; } = "Some Name";
See: C# 6: First reactions , Initializers for automatically implemented properties - By Jon Skeet
In Version of C# (6.0) & greater, you can do :
For Readonly properties
public int ReadOnlyProp => 2;
For both Writable & Readable properties
public string PropTest { get; set; } = "test";
In current Version of C# (7.0), you can do : (The snippet rather displays how you can use expression bodied get/set accessors to make is more compact when using with backing fields)
private string label = "Default Value";
// Expression-bodied get / set accessors.
public string Label
{
get => label;
set => this.label = value;
}
In C# 9.0 was added support of init keyword - very useful and extremly sophisticated way for declaration read-only auto-properties:
Declare:
class Person
{
public string Name { get; init; } = "Anonymous user";
}
~Enjoy~ Use:
// 1. Person with default name
var anonymous = new Person();
Console.WriteLine($"Hello, {anonymous.Name}!");
// > Hello, Anonymous user!
// 2. Person with assigned value
var me = new Person { Name = "#codez0mb1e"};
Console.WriteLine($"Hello, {me.Name}!");
// > Hello, #codez0mb1e!
// 3. Attempt to re-assignment Name
me.Name = "My fake";
// > Compilation error: Init-only property can only be assigned in an object initializer
In addition to the answer already accepted, for the scenario when you want to define a default property as a function of other properties you can use expression body notation on C#6.0 (and higher) for even more elegant and concise constructs like:
public class Person{
public string FullName => $"{First} {Last}"; // expression body notation
public string First { get; set; } = "First";
public string Last { get; set; } = "Last";
}
You can use the above in the following fashion
var p = new Person();
p.FullName; // First Last
p.First = "Jon";
p.Last = "Snow";
p.FullName; // Jon Snow
In order to be able to use the above "=>" notation, the property must be read only, and you do not use the get accessor keyword.
Details on MSDN
In C# 6 and above you can simply use the syntax:
public object Foo { get; set; } = bar;
Note that to have a readonly property simply omit the set, as so:
public object Foo { get; } = bar;
You can also assign readonly auto-properties from the constructor.
Prior to this I responded as below.
I'd avoid adding a default to the constructor; leave that for dynamic assignments and avoid having two points at which the variable is assigned (i.e. the type default and in the constructor). Typically I'd simply write a normal property in such cases.
One other option is to do what ASP.Net does and define defaults via an attribute:
http://msdn.microsoft.com/en-us/library/system.componentmodel.defaultvalueattribute.aspx
My solution is to use a custom attribute that provides default value property initialization by constant or using property type initializer.
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class InstanceAttribute : Attribute
{
public bool IsConstructorCall { get; private set; }
public object[] Values { get; private set; }
public InstanceAttribute() : this(true) { }
public InstanceAttribute(object value) : this(false, value) { }
public InstanceAttribute(bool isConstructorCall, params object[] values)
{
IsConstructorCall = isConstructorCall;
Values = values ?? new object[0];
}
}
To use this attribute it's necessary to inherit a class from special base class-initializer or use a static helper method:
public abstract class DefaultValueInitializer
{
protected DefaultValueInitializer()
{
InitializeDefaultValues(this);
}
public static void InitializeDefaultValues(object obj)
{
var props = from prop in obj.GetType().GetProperties()
let attrs = prop.GetCustomAttributes(typeof(InstanceAttribute), false)
where attrs.Any()
select new { Property = prop, Attr = ((InstanceAttribute)attrs.First()) };
foreach (var pair in props)
{
object value = !pair.Attr.IsConstructorCall && pair.Attr.Values.Length > 0
? pair.Attr.Values[0]
: Activator.CreateInstance(pair.Property.PropertyType, pair.Attr.Values);
pair.Property.SetValue(obj, value, null);
}
}
}
Usage example:
public class Simple : DefaultValueInitializer
{
[Instance("StringValue")]
public string StringValue { get; set; }
[Instance]
public List<string> Items { get; set; }
[Instance(true, 3,4)]
public Point Point { get; set; }
}
public static void Main(string[] args)
{
var obj = new Simple
{
Items = {"Item1"}
};
Console.WriteLine(obj.Items[0]);
Console.WriteLine(obj.Point);
Console.WriteLine(obj.StringValue);
}
Output:
Item1
(X=3,Y=4)
StringValue
little complete sample:
using System.ComponentModel;
private bool bShowGroup ;
[Description("Show the group table"), Category("Sea"),DefaultValue(true)]
public bool ShowGroup
{
get { return bShowGroup; }
set { bShowGroup = value; }
}
You can simple put like this
public sealed class Employee
{
public int Id { get; set; } = 101;
}
In the constructor. The constructor's purpose is to initialized it's data members.
private string name;
public string Name
{
get
{
if(name == null)
{
name = "Default Name";
}
return name;
}
set
{
name = value;
}
}
Have you tried using the DefaultValueAttribute or ShouldSerialize and Reset methods in conjunction with the constructor? I feel like one of these two methods is necessary if you're making a class that might show up on the designer surface or in a property grid.
Use the constructor because "When the constructor is finished, Construction should be finished". properties are like states your classes hold, if you had to initialize a default state, you would do that in your constructor.
To clarify, yes, you need to set default values in the constructor for class derived objects. You will need to ensure the constructor exists with the proper access modifier for construction where used. If the object is not instantiated, e.g. it has no constructor (e.g. static methods) then the default value can be set by the field. The reasoning here is that the object itself will be created only once and you do not instantiate it.
#Darren Kopp - good answer, clean, and correct. And to reiterate, you CAN write constructors for Abstract methods. You just need to access them from the base class when writing the constructor:
Constructor at Base Class:
public BaseClassAbstract()
{
this.PropertyName = "Default Name";
}
Constructor at Derived / Concrete / Sub-Class:
public SubClass() : base() { }
The point here is that the instance variable drawn from the base class may bury your base field name. Setting the current instantiated object value using "this." will allow you to correctly form your object with respect to the current instance and required permission levels (access modifiers) where you are instantiating it.
public Class ClassName{
public int PropName{get;set;}
public ClassName{
PropName=0; //Default Value
}
}
This is old now, and my position has changed. I'm leaving the original answer for posterity only.
Personally, I don't see the point of making it a property at all if you're not going to do anything at all beyond the auto-property. Just leave it as a field. The encapsulation benefit for these item are just red herrings, because there's nothing behind them to encapsulate. If you ever need to change the underlying implementation you're still free to refactor them as properties without breaking any dependent code.
Hmm... maybe this will be the subject of it's own question later
class Person
{
/// Gets/sets a value indicating whether auto
/// save of review layer is enabled or not
[System.ComponentModel.DefaultValue(true)]
public bool AutoSaveReviewLayer { get; set; }
}
I know this is an old question, but it came up when I was looking for how to have a default value that gets inherited with the option to override, I came up with
//base class
public class Car
{
public virtual string FuelUnits
{
get { return "gasoline in gallons"; }
protected set { }
}
}
//derived
public class Tesla : Car
{
public override string FuelUnits => "ampere hour";
}
I think this would do it for ya givng SomeFlag a default of false.
private bool _SomeFlagSet = false;
public bool SomeFlag
{
get
{
if (!_SomeFlagSet)
SomeFlag = false;
return SomeFlag;
}
set
{
if (!_SomeFlagSet)
_SomeFlagSet = true;
SomeFlag = value;
}
}
I need to determine which object my code is working with at a certain point and write out only the properties specific to that class. I cannot figure out how to do it. I was told I can do it, but I cannot figure it out. Can someone please show me how to determine which object I am working with and write the properties specific to that class only?
I've looked at other questions asked, but am not smart enough to make it fit my example.
Below, I've re-created an example of the code I am working with. I can see all the code, but I am allowed to only work in one method (for this example called "MethodIAmWorkingIn"). Only modifications I am allowed to make are in that method.
public class Program
{
static void Main(string[] args)
{
TestDetailsAndResultsContainer container = new TestDetailsAndResultsContainer();
DerivedClass1 derivedClass1 = new DerivedClass1();
derivedClass1.DerivedClass1Prop1 = "DerivedClass1Prop1";
derivedClass1.DerivedClass1Prop2 = "DerivedClass1Prop2";
DerivedClass2 derivedClass2 = new DerivedClass2();
derivedClass2.DerivedClass2Prop1 = "DerivedClass2Prop1";
derivedClass2.DerivedClass2Prop2 = "DerivedClass2Prop2";
container.TestDetails.Add(derivedClass1);
container.TestDetails.Add(derivedClass2);
TestResult testResult = new TestResult();
testResult.TestResultProp1 = "TestResultProp1";
testResult.TestResultProp2 = "TestResultProp2";
container.Data.Add(testResult);
Program p = new Program();
p.MethodIAmWorkingIn(container);
}
private void MethodIAmWorkingIn(TestDetailsAndResultsContainer container)
{
// I need to see if the container variable holds a DerivedClass1 or DerivedClass2 object.
foreach (var result in container.TestDetails)
{
var classINeedToDetermine = container.TestDetails.FirstOrDefault(m => m.TestDetailsProp1 == result.TestDetailsProp1);
if (classINeedToDetermine is DerivedClass1)
{
classINeedToDetermine = result as DerivedClass1;
}
else if (classINeedToDetermine is DerivedClass2)
{
classINeedToDetermine = result as DerivedClass2;
}
// Now I need to use the classINeedToDetermine object and write its specific properties.
// ???????????????????? I am stuck at this point ??????????????????
// I need to write one or the other below. Can this be done?
// If it is DerivedClass1, I need to write out those properties only.
Console.WriteLine(classINeedToDetermine.DerivedClass1Prop1);
Console.WriteLine(classINeedToDetermine.DerivedClass1Prop2);
// OR
// If it is DerivedClass2, I need to write out those properties only.
Console.WriteLine(classINeedToDetermine.DerivedClass2Prop1);
Console.WriteLine(classINeedToDetermine.DerivedClass2Prop2);
}
}
}
public class TestDetailsAndResultsContainer
{
public TestDetailsAndResultsContainer()
{
this.Data = new List<TestResult>();
this.TestDetails = new List<TestDetails>();
}
public List<TestDetails> TestDetails { get; set; }
public List<TestResult> Data { get; set; }
}
public abstract class TestDetails
{
public string TestDetailsProp1 { get; set; }
public string TestDetailsProp2 { get; set; }
}
public class TestResult
{
public string TestResultProp1 { get; set; }
public string TestResultProp2 { get; set; }
}
public class DerivedClass1 : TestDetails
{
public string DerivedClass1Prop1 { get; set; }
public string DerivedClass1Prop2 { get; set; }
}
public class DerivedClass2 : TestDetails
{
public string DerivedClass2Prop1 { get; set; }
public string DerivedClass2Prop2 { get; set; }
}
The as keyword does not do what you think it does.
Your classINeedToDetermine is a variable of type TestDetails. You can assign a subclass instance to it, but you still cannot access that subclasses specific properties via that variable.
You just need some scope:
if (classINeedToDetermine is DerivedClass1)
{
var derived1 = (DerivedClass1)result;
Console.WriteLine(derived1.DerivedClass1Prop1);
// etc
}
else if (classINeedToDetermine is DerivedClass2)
{
var derived2 = (DerivedClass2)result;
Console.WriteLine(derived2.DerivedClass2Prop1);
// etc
}
This kind of thing is not fun to maintain though.
Another way of doing it would be to use polymorphism: Your TestDetails class could define an abstract method called PrintProperties, and all your subclasses could implement it.
Then you just have to call PrintProperties on every object, without worrying about which subclass they are.
I'm not completely sure on what objects mean what here, but it should be something like this:
foreach(var v in result.GetType().GetProperties())
{
if(v.DeclaringType == result.GetType())
{
Console.WriteLine(v.GetValue(result));
}
}
Again, I wasn't totally clear on your usage of classINeedToDetermine versus result, but you can change those as you see so fit. I chose result as my example since classINeedToDetermine was null by that part of your code.
And by the way, this section of your code is redundant. It should be removed for clarity and efficiency.
if (classINeedToDetermine is DerivedClass1)
{
classINeedToDetermine = result as DerivedClass1;
}
else if (classINeedToDetermine is DerivedClass2)
{
classINeedToDetermine = result as DerivedClass2;
}
Edit:
On the other hand, if performance is more important to you than being scalable (read: if you know or have control over all the classes you'll be dealing with), you could use this is section to do something less, I hate to use this word for this, "generic."
if (classINeedToDetermine is DerivedClass1)
{
var typed = (DerivedClass1)result;
Console.WriteLine(typed.DerivedClass1Prop1);
Console.WriteLine(typed.DerivedClass1Prop2);
}
else if (classINeedToDetermine is DerivedClass2)
{
var typed = (DerivedClass2)result;
Console.WriteLine(typed.DerivedClass2Prop1);
Console.WriteLine(typed.DerivedClass2Prop2);
}
You can use Type.GetProperties to get all Properties for your class. Then you need to get the MethodInfo for the Get method via GetMethod.
Once you have the MethodInfo you can call Invoke, passing in your Derived Class. You can write then write the result to the Console.
I've got a question about getting the values from a constructor in a generic way.
namespace myTestNamespace
{
Public Class myTestClass()
{
Public myTestClass(int myInt,bool myBool, double myDouble)
{
//do / set something
}
Public myTestClass(int myInt,bool myBool)
{
//do / set something
}
}
}
Using (what you need);
Using myTestNamespace;
namespace MyIWannaLookForTheParametersName
{
Public Class MyLookUpClass()
{
Public void DoSomething()
{
List<object> myList = new List<object>();
myTestClass _ myTestClass = new myTestClass(1,true,2.5);
object mySaveObject = myTestClass;
mylist.Add(mySaveObject);
//how do I get the info from the right constructor
//(I used the one with 3 parameters_
//what was the value of myInt, myBool and myDouble
//how can I make it generic enough, so it will work with other classes with
// different constructors ass well?
}
}
}
Questions about intent aside, there's no generic way for you to do this. Information about what methods have been called and what values were supplied is not saved automatically. You are, of course, perfectly able to keep track of these things yourself, but you would have to write each class to do this explicitly.
Doing this in a generic way is asking for trouble. What if I did this?
public class Foo
{
public string Name { get; set; }
}
public class Bar
{
public Bar(Foo foo)
{
// ...
}
}
Then suppose I called it in this way:
Foo f = new Foo();
f.Name = "Jim";
Bar b = new Bar(f);
f.Name = "Bob";
Now, if such a generic system existed, what would be the value of foo for the Bar constructor? Either it reports "Bob" (which is what the value for Name is on the instance of Foo that was supplied), or it reports "Jim", meaning that the runtime or library would essentially have to be smart enough to make a deep copy of the object so that the state is not changed.
The bottom line is this: if you need access to the parameters passed to the constructor (or any other function), you'll have to store them somewhere explicitly.
You can't get thevalues from the constructor. You need to first place them in a property or a field within your class. The example you provided is a poor use of generics. You wouldbe better off placing the constructor values into properties and creating an interface with those properties.
I got what I needed with this method:
private static ParameterSettings[] GetListOfParametersFromIndicator(object indicatorClass, int loopId, myEnums.ParaOrResult paraOrResult)
{
return (from prop in indicatorClass.GetType().GetProperties()
let loopID = loopId
let Indicator = indicatorClass.GetType().Name
let value = (object)prop.GetValue(indicatorClass, null)
where prop.Name.Contains("_Constr_")
select new ParameterSettings { ParaOrResult=paraOrResult, LoopID= loopId, Indicator= Indicator, ParaName= prop.Name, Value= value }).ToArray();
}
where ParameterSettings is:
public struct ParameterSettings
{
public myEnums.ParaOrResult ParaOrResult { get; set; }
public int LoopID { get; set; }
public string Indicator { get; set; }
public string ParaName { get; set; }
public object Value { get; set; }
}
This info is ok for me. Thanks for the replies.
Regards,
Matthijs