C# - Passing concrete constructor method to create 'child' classes - c#

I have a Class X wich uses a Class Y. The X creates the Y, but X must create Y with THE SAME constructor method was used to create instance-Y passed to X.
It is not a Clone, because I want a NEW object-Y not equals to values of instance-Y passed to X.
It is not a instance because I do not want the SAME object-Y what is pased as instance-Y to X.
I would like to pass the "constructor method and parameters" of class Y to class X and, with this information, create the new Y-instance using the ctor-method-passed.
And I don't want to devel all 'Class Y' constructor logic because, in this case both of them will be very highly coupled.
I have done a little spike to explain myself a bit better.
Thanks.
using System;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
TheSon son1 = new TheSon();
son1.TheValue = "Another Value";
TheFather<TheSon> father1 = new TheFather<TheSon>(son1);
Console.WriteLine("Result is {0}:", "Empty constructor".Equals(father1.MySon.TheValue));
Console.WriteLine("\tbecause prop. must be: '{0}' and it is: '{1}'", "Empty constructor", father1.MySon.TheValue);
}
public class TheFather<T> where T: TheSon
{
public TheSon MySon { get; set; }
public TheFather(T mySon) {
// I would like to NOT use the same object but
// use the constructor that was used to build the passed object-instance.
//
// Or perhaps pass a concrete TheSon constructor to the 'TheFather'...
this.MySon = (TheSon)mySon;
}
}
public class TheSon
{
public string TheValue { get; set; }
public TheSon()
{
this.TheValue = "Empty constructor";
}
public TheSon(string value)
{
this.TheValue = value;
}
public TheSon(string value, int element)
{
this.TheValue = value + "-> " + Convert.ToString(element);
}
}
}
}
=========SOLUTION:
Adding this constructor to the TheFather class:
public TheFather(Func<T> sonFactory)
{
this.MySon = (T)sonFactory();
}
And with this example:
static void Main(string[] args)
{
// Uncomment one of this to change behaviour....
//Func<TheSon> factory = () => new TheSon();
Func<TheSon> factory = () => new TheSon("AValue");
//Func<TheSon> factory = () => new TheSon("AValue", 1);
TheFather<TheSon> father1 = new TheFather<TheSon>(factory);
Console.WriteLine("Result is {0}:", "AValue".Equals(father1.MySon.TheValue));
Console.WriteLine("\tbecause prop. must be: '{0}' and it is: '{1}'", "AValue", father1.MySon.TheValue);
}
Works like a charm.... :-)
Thanks...

You can simply use a factory to create TheSon objects:
Func<TheSon> factory = () => new TheSon(); // creates one with default ctor
This way you can get a new object each time, but created in exactly the same way (this is not limited to a constructor; you can also include any additional code you want). Use it like this:
var oneSon = factory(); // creates one son
var secondSon = factory(); // creates another with the same constructor
var father = new TheFather(factory()); // ditto
Update: You can also change TheFather's constructor to accept a factory if you want to create TheSon inside TheFather. For example:
public TheFather(Func<T> sonFactory) {
this.MySon = (TheSon)sonFactory();
}
and
var father = new TheFather(factory);

Related

"Reflection": Method with "params" does not initialize my custom Objects

I have a custom class from which I create over 200 objects for a Gui.
In my main class with my business logic I want to apply the instantiation of all my objects, attach them to an event handler and set their Name. Instead of doing all this by hand for every object I thought to create a method that takes as parameters a "params" list of my objects. The problem is that this method seems to work out a "copy" of my objects instead the reference of those. What I have so far is:
My object base class:
public class MyObject
{
... // this works
}
My business class now (what works):
public class Logic
{
public MyObject Object001, Object002,... Object200; // note that they are not instantiated yet
public Logic()
{
Object001 = new MyObject();
Object001.Name = nameof(Object001);
Object001.PropertyChanged += (s, e) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
Object002 = new MyObject();
Object002.Name = nameof(Object002);
Object002.PropertyChanged += (s, e) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
...
Object200 = new MyObject();
Object200.Name = nameof(Object200 );
Object200.PropertyChanged += (s, e) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
}
}
My desired business class (what has not worked):
public class Logic
{
public MyObject Object001, Object002,... Object200; // note that they are not instantiated yet
public Logic()
{
InstantiateAllObjects(Object001, Object002, ..., Object200); // here my 200 objects!
}
private void InstantiateAllObjects(params MyObject[] list)
{
for (int i=0; i<list.Length; i++)
{
// if(list[i]==Object001) Console.WriteLine("Object001== null?: " + (Object001 == null)); --> this executes for EVERY object, instead for only Object001!!
MyObject obj = list[i];
obj = new MyObject();
obj.Name = nameof(obj); // why "nameof(list[i])" didn't work?
obj.PropertyChanged += (s, e) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
// if (list[i] == Object001) Console.WriteLine("Object001== null?: " + (Object001== null)); --> this executes again for EVERY object AND (Object001== null) is ALWAYS true!!
}
}
}
Can anybody explain me why my method seems not to create my objects?
Thanks in advance for your time and help!
EDIT
In my opinion, the problem seems to be i have to pass a reference to these declared objects in the 'params' list of my method... is it possible? how? I tried to use the modifiers "out" and "ref" near the "params" in the method, but with "params" seems not to be possible...
EDIT2
Following some suggestions I created in my class: logic an object list:
MyObject[] list = new MyObject[] { Object001, Object002, ..., Object200 };
InstantiateAllObjects(ref list);
and modified my method as per private void InstantiateAllObjects(ref MyObject[] list) iterating inside over list[i], but unfortunately with the same wrong result...
and also tried
List<MyObject> list = new List<MyObject>() { Object001, Object002, ..., Object200 };
InstantiateAllObjects(ref list);
and modified my method as per private void InstantiateAllObjects(ref List<MyObject> list) iterating inside over list[i], but unfortunately also with the same wrong result...
Forget about all those Object001, Object002 etc fields. Just use a new List<MyObject>() and add your objects into it inside the for-loop in your InstantiateAllObjects.
public class Logic
{
private readonly List<MyObject> allMyObjects; // note that they still are not instantiated yet
public Logic()
{
cont int amount = 200;
allMyObjects = new List<MyObject>(amount); // reserve space, but all are still null
InstantiateAllObjects(allMyObjects, amount);
}
private void InstantiateAllObjects(List<MyObject> list, int amount)
{
for (int i=0; i<amount; i++)
{
MyObject obj = new MyObject();
obj.Name = "Object" + (i+1).ToString("000");
obj.PropertyChanged += (s, e) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
list.Add(obj); // place the newly created object in the list
}
}
}
Your original test if(list[i]==Object001) fires every time, because both list[i] (for every i) and Object001 are null.
Also note that you can have multiple references to one instance of your MyObject (this already happens when you pass the reference as parameter to some method). The fact that one of those is called "Object001" is not important and in fact unknown to the instance. That is why nameof(list[i]) cannot return "Object001".
Another try, based on Hans Kesting'a answer and anna's statement
2) the name of every object will be (very) different one to another,
it is here just that i used the indexes 001...200 to concept the idea,
To avoid any trouble with accessing the fields and to regard what you wrote in the comments, no getter/setter is used and everything is public. Not the best approach but well, compared to handling 200 separate objects...
public class Logic
{
public List<MyObject> MyObjectList;
public List<string> MyObjectNames;
public Logic()
{
var anotherClass = new AnotherClass();
MyObjectNames = new List<string>() {"Object01", "Object02", "Object03"}; // either add your names here...
MyObjectNames.Add("Object04"); // or add additional names this way
//MyObjectNames.AddRange(anotherNameList); // or add another list or use Linq or whatever
MyObjectList = anotherClass.InstantiateAllObjects(MyObjectNames);
}
}
public class AnotherClass
{
public List<MyObject> InstantiateAllObjects(List<string> nameList)
{
var objectList = new List<MyObject>(nameList.Count);
foreach (var name in nameList)
{
objectList.Add(new MyObject(){Name = name});
}
return objectList;
}
}
Does this meet your requirements?
If you prefer a Dictionary, it's similar:
public class Logic
{
public Dictionary<string, MyObject> MyObjectDict;
public List<string> MyObjectNames;
public Logic()
{
var anotherClass = new AnotherClass();
MyObjectNames = new List<string>() { "Object01", "Object02", "Object03" }; // either add your names here...
MyObjectNames.Add("Object04"); // or add additional names this way
//MyObjectNames.AddRange(anotherNameList); // or add another list or use Linq or whatever
MyObjectDict = anotherClass.InstantiateAllObjects(MyObjectNames);
// objects in dict can be accessed directly by their names:
var object01 = MyObjectDict["Object01"];
}
}
// You can access in derived classes or any other classes
public class DerivedLogic : Logic
{
public void SomeFunc()
{
var object01 = MyObjectDict["Object01"];
}
public void SomeOtherFunc(string objectName)
{
var object01 = MyObjectDict[objectName];
}
}
public class AnotherClass
{
public Dictionary<string, MyObject> InstantiateAllObjects(List<string> nameList)
{
var objectList = new Dictionary<string, MyObject>(nameList.Count);
foreach (var name in nameList)
{
// check if object with name does not already exist.
if(!objectList.ContainsKey(name)
{
// For your property changed assignment, you can separate the object creation and DIctionary/List assignment
var obj = new MyObject() { Name = name };
obj.PropertyChanged += (s, e) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
objectList.Add(name, obj);
}
// else .... doe something
}
return objectList;
}
}
All of those arguments are being passed by value. You're passing the references, but the InstantiateAllObjects method can't change the values of any of the fields.
Simplifying this a bit, it's a little like having code like this:
string x = "Original value";
// This copies the value of x into the array
string[] array = new string[] { x };
// This changes the array element, but doesn't affect the x variable at all
array[0] = "Different value";
// Still prints "Original value", because x hasn't changed
Console.WriteLine(x);
As noted in comments, using a list is likely to be a much better approach than lots of different fields here.
Use a class (can be the same as the one with the logic) to hold your MyObject Fields, pass it into a function that uses reflection to populate the fields.
// Class with Fields
public class MyObjectCollection
{
public MyObject Object001;
public MyObject ObjTwo;
}
public class Logic
{
// Init logic
public void Initialise(object controls)
{
Type targType = typeof(MyObject);
var t = controls.GetType();
// iterate all fields of type MyObject
foreach(var fi in t.GetFields().Where(f=>f.FieldType == targType))
{
// initialise as required.
var o = new MyObject();
o.Name = fi.Name;
fi.SetValue(controls, o);
}
}
}
If the field name is not enough for the object name you could use Attributes on the fields to direct initialisation.
This should do the job. It is a reflection-based approach and what it does is:
find all public instance fields
use only those whose name contains Object
create a new instance of MyObject and store that in the field
public void InstantiateAllObjects()
{
foreach (FieldInfo field in this.GetType()
.GetFields(BindingFlags.Public | BindingFlags.Instance)
.Where(x => x.Name.Contains("Object")))
{
MyObject obj= new MyObject();
obj.PropertyChanged += (s, e) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
field.SetValue(this, obj);
}
}
If you add above method to your Logic class, you can then call it without having to explicitly pass Object001, Object002 etc. to it.
public class Logic
{
public MyObject Object001, Object002, ... Object200;
public Logic()
{
this.InstantiateAllObjects();
}
}
EDIT: Differently named fields
If the fields do not share a common prefix (that is, if they are not all named Object + ...), there are other ways to get the fields:
.Where(x => x.FieldType == typeof(MyObject))
yields only the fields whose type is MyObject.
You could also create an attribute
[AttributeUsage(AttributeTargets.Field)]
public sealed class FieldIWantToSetAttribute : Attribute { }
and apply that to all fields you want to set, e.g.
public class Logic
{
[FieldIWantToSet] public MyObject Object001; // will be set
[FieldIWantToSet] public MyObject Foo; // will be set
public MyObject Bar; // will not be set
}
Then, change the Where to
.Where(x => x.GetCustomAttribute<FieldIWantToSet>() != null)
Please however note that 1. you should definitely use caching. Reflection without caching is expensive, and 2. please overthink your design - why exactly do you feel the need to expose 200 fields for every other class to see?

C# Lists - do I use Class Methods (Get/ Set etc) again once the data is in a list?

A quick question on OOP. I am using a list together with a class and class constructor. So I use the class constructor to define the data set and then add each record to my list as the user creates them.
My questions is once the data is in the list and say I want to alter something is it good practice to find the record, create an instance using that record and then use my class methods to do whatever needs doing - and then put it back in the list?
For example below I have my class with constructor. Lets say I only want the system to release strCode if the Privacy field is set to public. Now just using Instances I would use for example Console.WriteLine(whateverproduct.ProductCode) but if the record is already in a list do i take it out of the list - create an instance and then use this method?
class Product
{
private String strCode;
private Double dblCost;
private Double dblNet;
private String strPrivacy;
public Product(String _strCode, Double _dblCost, Double _dblNet, String _strPrivacy)
{
strCode = _strCode;
dblCost = _dblCost;
dblNet = _dblNet;
strPrivacy = _strPrivacy;
}
public string ProductCode
{
get
{
if (strPrivacy == "Public")
{
return strCode;
}
else
{
return "Product Private Can't release code";
}
}
}
Lets say we have the following:
public class Test
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Amount { get; set; }
private string _test = "Some constant value at this point";
public string GetTest()
{
return _test;
}
public void SetTest()
{
//Nothing happens, you aren't allow to alter it.
//_test = "some constant 2";
}
}
public class Program
{
public static void Main(string[] args)
{
List<Test> listOfTest = new List<Test>()
{
new Test() {Id = 0, Name = "NumberOne", Amount = 1.0M},
new Test() {Id = 1, Name = "NumberTwo", Amount = 2.0M}
};
Test target = listOfTest.First(x => x.Id == 0);
Console.WriteLine(target.Name);
target.Name = "NumberOneUpdated";
Console.WriteLine(listOfTest.First(x => x.Id == 0).Name);
Console.WriteLine(listOfTest.First(x => x.Id == 0).GetTest());//This will alsways be "Some constant value at this point";
Console.ReadLine();
}
}
Technically you could do away with the SetTest method entirely. However, I included it to demonstrate, what it would look like, if you wanted to alter _test.
You don't want to ever create a new instance of a class, you already have an instance of. you can just alter the class where it is allowed by the author of the class, where you need to. And keep that class reference for as long as you need it. Once you are done, the reference will be garbage collected, once the program finds no active reference to your object(instance).

How can I refer to a field of a class as some object, then use that object to obtain the field's value later?

Say I have the following code
public class FooClass
{
public int A { get; set; } = 0;
public int B { get; set; } = 0;
}
public class BarClass
{
public int X { get; set; } = 0;
public int Y { get; set; } = 0;
}
public class MyClass
{
int z = 0;
public FooClass Foo { get; set; } = new FooClass();
public BarClass Bar { get; set; } = new BarClass();
public static void MyMethod()
{
// List of MyClass objects
var myList = Enumerable.Range(1, 10).Select(_ => new MyClass()).ToList();
// Some flags set elsewhere
bool getFooAValues = true;
bool getBarYValues = true;
bool getClassZValues = true;
// Some statements that collects "field referecnes" of MyClass
var classFieldReferenceList = new List<...>();
if (getFooAValues)
classFieldReferenceList.Add(...);
if (getBarYValues)
classFieldReferenceList.Add(...);
if (getClassZValues)
classFieldReferenceList.Add(...);
// For each field reference
classFieldReferenceList.ForEach(classFieldRef =>
{
// For each class
myList.ForEach(myClassInst =>
{
// "Select"/"Apply" the reference to get the field value
var fieldValue = myClassInt.getTheFieldReferenceValue(classFieldRef);
// Do something with field value...
return fieldValue;
});
// Do something with the list of field values...
});
}
}
In this code, specifically in MyMethod, I create a list of MyClass objects. This class has a few fields, some are simply primitive types, some are instances of other classes. How can I refer to or address these fields in the form of some object I can pass around?
For example, I began writing code, akin to the following
public static void MyMethod()
{
// List of MyClass objects
var myList = Enumerable.Range(1, 10).Select(_ => new MyClass()).ToList();
// Some flags set elsewhere
bool getFooAValues = true;
bool getBarYValues = true;
bool getClassZValues = true;
if (getFooAValues)
{
var Avalues = myList.Select(myClassInst => myClassInst.Foo.A);
// Do Action X to list of values
}
if (getBarYValues)
{
var Yvalues = myList.Select(myClassInst => myClassInst.Bar.Y);
// Do Action X to list of values
}
if (getClassZValues)
{
var Zvalues = myList.Select(myClassInst => myClassInst.z);
// Do Action X to list of values
}
}
Where //Do Action X was quite a few lines of code that I would perform to each set of values (Plotting values on a plot, flags represent showing plot line or not). Though, I don't really want duplicate that code for each possible field I could refer/address within MyClass. Thus, I want to refer to a field by some "object" then "apply" that object to an instance of MyClass later to get the value of the field, if that makes sense.
I feel like this might be akin to defining a delegate? Though the delegate would be specific to some class structure?.. Or maybe there is some simple solution I have confused myself out of finding.
You can use Func<MyClass,object> delegate:
var classFieldReferenceList = new List<Func<MyClass,object>>();
if (...)
classFieldReferenceList.Add(m => m.Foo.A);
if (...)
classFieldReferenceList.Add(m => m.Foo.B);
if (...)
classFieldReferenceList.Add(m => m.Bar.X);
if (...)
classFieldReferenceList.Add(m => m.Bar.Y);
This is not ideal because object is used as the most common denominator, but that would be required for a "mixed bag" of types.
In your second example you could get away with a generic method:
private void DoActionsOnSelectedFields<T>(IEnumerable<MyClass> data, Func<MyClass,T> selector) {
foreach (T val in data.Select(selector)) {
... // Perform some common action
}
}

Display all properties and if property is a class display its properties

I am using reflection to loop through classes of a DLL and display the properties of that class; but there are cases where the property is another class, and I need to loop through that property's properties and display them.
What I currently have will only display the properties, and not sub properties:
treeView1.Nodes.Clear();
Assembly assembly = Assembly.LoadFrom(#"Path\Domain.dll");
int count = 0;
foreach (Type type in assembly.GetTypes().Where(t => t.IsClass))
{
var node = treeView1.Nodes.Add(type.FullName);
var x = ((System.Reflection.TypeInfo)((assembly.GetTypes()))[count]).DeclaredProperties;
x.ToList().ForEach(item => treeView1.Nodes[count].Nodes.Add(item.Name));
count++;
}
}
Any help to display sub properties
What you can do is recursively do an iteration. Create a member that displays the properties, and call the member itself with the sub properties. This might give you an idea:
namespace ConsoleApp6
{
class Program
{
static void Main(string[] args)
{
MyClass myObject = new MyClass();
ListProperties(myObject.GetType(), 2);
Console.ReadLine();
}
static void ListProperties(Type type, int ident)
{
var x = type.GetProperties();
Console.WriteLine(new String(' ', ident) + "Class: " + type.FullName);
int newIdent = ident += 2;
x.ToList().Where(item => item.PropertyType.IsClass).ToList().ForEach(item => ListProperties(item.PropertyType, newIdent));
}
}
class MyClass
{
public SubClass PropertySubClass1 { get; set; }
public SubClass PropertySubClass2 { get; set; }
}
class SubClass
{
public SubSubClass PropertySubSubClass { get; set; }
}
class SubSubClass
{
}
}
In this code fragment in the Main you see that a member ListProperties is called. In this member, the property type is listed, and then it calls itself (in the last line for the sub properties).
Instead of 'ident' for pretty-printing in this example, you have to add the node text in a similar way.
The output of this example is:

Create an instance for appened string

I am trying to create a generic method where I have to append some string to Generic data type and create instance of that appended string.
Eg:
I have two classes
Object1,
Object2,
Object3,
Object4,....Object100 and
Object1Address,
Object2Address,
Object3Address,
Object4Address,....Object100Address
Right now I have method for each Object like MethodObject1(), MethodObject2(),MethodObject3(), MethodObject4()
MethodObject1()
{
Object1 object = new Object1();
Object1Address objectAddress = new Object1Address();
TestMethod();
}
MethodObject2()
{
Object2 object = new Object2();
Object2Address objectAddress = new Object2Address();
TestMethod();
}
MethodObject3()
{
Object3 object = new Object3();
Object3Address objectAddress = new Object3Address();
TestMethod();
}
MethodObject4()
{
Object4 object = new Object4();
Object4Address objectAddress = new Object4Address();
TestMethod();
}
.
.
.
MethodObject100()
{
Object100 object = new Object100();
Object100Address objectAddress = new Object100Address();
TestMethod();
}
But there are around 100 object so I have to create 100 methods in similar way.
So I thought of creating an generic method.
There is a method which returns only the an array of Object name from an XML file.
Eg: ObjectName[] objectName = GetObjects(); // Returns an array of Object Names from XML file so that
objectName[0] = Object1;
objectName[1] = Object2;
objectName[2] = Object3;
objectNmae[3] = Object4;
.
.
.
objectName[99] = Object100;
I am looping through objectName array
foreach(var objectItem in objectName)
{
MethodName<objectItem>();
}
In my generic method I can create an instance of object, like
MethodName<T>()
{
T t = new T();
Some how I have to get the name of T and append Address to T
So that I can create TAddress instance like
TAddress tAddress = new TAddress();
}
Is there any way I can do this using C sharp?
Even with the edits, I still can't follow what you're asking for. However, in an attempt to make forward progress, I'll offer an answer that might at least help isolate what you want.
public class ThingWithAddress<T>
{
public T Item { get; private set; }
public string Address { get; private set; }
public static ThingWithAddress<T> Create(T item, string address)
{
return new ThingWithAddress<T>
{
Item = item,
Address = address,
};
}
}
Then you can get an object that has both a person and address like:
Person person = ...;
var personWithAddress = ThingWithAddress.Create(person, "some address");
Don't take this the wrong way, but you might need to learn enough C# to formulate a good question, since as it stands, the question seems confusing and inconsistent.
Your case doesn't really require a generic method.
What you probably want is to override ToString method in your classes.
class PersonAddress()
{
public override string ToString()
{
return string.Concat(this.Street, ", ", this.City, ", ", this.Postcode);
}
}
class Person()
{
public override string ToString()
{
return string.Concat(this.Name, ", ", this.PersonAddress.ToString());
}
}
Notice how Person class reuses PersonAddress.ToString() method.

Categories