I have a big class with a lot of properties (BigClass). I need to make a new class (SmallClass) with only some of those properties. This SmallClass must use all the overlapping properties from BigClass. What is the easiest way to do this without having to manually assign all the properties in the constructor of SmallClass like I do below:
class BigClass
{
public int A { get; }
public int B { get; }
public int C { get; }
public int D { get; }
public int E { get; }
public BigClass(int a, int b, int c, int d, int e)
{
A = a;
B = b;
C = c;
D = d;
E = e;
}
}
class SmallClass
{
public int A { get; }
public int B { get; }
public int C { get; }
public SmallClass(BigClass bigClass)
{
// I don't want to do all this manually:
A = bigClass.A;
B = bigClass.B;
C = bigClass.C;
}
}
Create a helper class:
public class Helper
{
public static void CopyItem<T>(BigClass source, T target)
{
// Need a way to rename the backing-field name to the property Name ("<A>k__BackingField" => "A")
Func<string, string> renameBackingField = key => new string(key.Skip(1).Take(key.IndexOf('>') - 1).ToArray());
// Get public source properties (change BindingFlags if you need to copy private memebers as well)
var sourceProperties = source.GetType().GetProperties().ToDictionary(item => item.Name);
// Get "missing" property setter's backing field
var targetFields = typeof(T).GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.SetField).ToDictionary(item => renameBackingField(item.Name));
// Copy properties where target name matches the source property name
foreach(var sourceProperty in sourceProperties)
{
if (targetFields.ContainsKey(sourceProperty.Key) == false)
continue; // No match. skip
var sourceValue = sourceProperty.Value.GetValue(source);
targetFields[sourceProperty.Key].SetValue(target, sourceValue);
}
}
}
Then in your SmallClass constructor:
public SmallClass(BigClass bigClass)
{
Helper.CopyItem(bigClass, this);
}
This should work even if you only have property getters.
You can make CopyItem to work with all types by changing its declaration;
public static void CopyItem<U, T>(U source, T target)
Related
I have two classes
public class ClassA
{
public int ID { get; set; }
public string Countries {get;set;}
public string City { get;set; }
}
and
public class ClassB
{
public int ID { get; set; }
public string Countries {get;set;}
public string Village{ get;set; }
}
These two classes are in another class
public class ComponentClass
{
public List<ClassA> classAObj { get; set; }
public List<ClassB> classBObj { get; set; }
}
The data for the ComponentClass is coming from third party where the data for ClassA and ClassB is similar in structure. "City" in ClassA will have data in comma separated values "Manchester,Sydney" etc similarly with Village as well with comma separated values.
Now I am building a customized object at business layer where I am iterating through each property of ComponentClass and extracting information.
Main()
{
ComponentClass[] c = //Data from 3rd party;
foreach(var data in c)
{
Parent p = new Parent();
if(data.classAObj.count > 0)
{
Star s = new Star();
s.Area = "infinite";
s.Color = "red";
List<string> sep = data.City.Split(',').Select(string.Parse).ToList();
foreach(var b in sep)
{
TinyStar t = new TinyStar();
t.smallD = b;
s.Values.Add(t);
}
p.Curves.Add(s);
}
if(data.classBObj.count > 0)
{
Star s2 = new Star();
s2.Area = "infinite";
s2.Color = "red";
List<string> sep = data.Village.Split(',').Select(string.Parse).ToList();
foreach(var b in sep)
{
TinyStar t = new TinyStar();
t.smallD = b;
s2.Values.Add(t);
}
p.Curves.Add(s);
}
}
}
In the code above , the two if statements are doing exactly the same operation apart from property names "City" and "Village". I want to simplify this by making use of any design pattern possibly strategy pattern which I just know theoretically or any other patterns.
Here is what I have tried :
public abstract class Base
{
public int ID { get; set; }
public string Countries {get;set;}
}
public class ClassA : Base
{
public string City { get;set; }
}
public class ClassB : Base
{
public string Village{ get;set; }
}
I want to make it as a common factory method which will do the looping and build the object for me for avoiding duplication of the code
public void CommonMethod(Base)
{
// How do I differentiate the properties for looping
}
If the goal is to reduce the code repetition, the two statements could be refactored into a single action as follows.
foreach(var data in c)
{
Parent p = new Parent();
Action<string> iAction = iString =>
{
Star s = new Star();
s.Area = "infinite";
s.Color = "red";
List<string> sep = iString.Split(',').Select(string.Parse).ToList();
foreach(var b in sep)
{
TinyStar t = new TinyStar();
t.smallD = b;
s.Values.Add(t);
}
p.Curves.Add(s);
}
if(data.classAObj.count > 0)
{
iAction(data.City);
}
if(data.classBObj.count > 0)
{
iAction(data.Village);
}
}
You have a same datatype properties for the both of the types in the json than you can create a single class to map it,
public class ClassA
{
public int ID { get; set; }
public string Countries {get;set;}
public string Areas{ get;set; }
}
public class ComponentClass
{
public List<ClassA> classAObj { get; set; }
public List<ClassA> classBObj { get; set; }
}
Main()
{
ComponentClass[] c = //Data from 3rd party;
foreach(var data in c)
{
Parent p = new Parent();
GetParent (p ,data.classAObj )
GetParent (p ,data.classBObj )
}
}
void GetParent (Parent p, ClassA classObj){
if(data.classAObj.count > 0)
{
Star s = new Star();
s.Area = "infinite";
s.Color = "red";
List<string> sep = data.Areas.Split(',').Select(string.Parse).ToList();
foreach(var b in sep)
{
TinyStar t = new TinyStar();
t.smallD = b;
s.Values.Add(t);
}
p.Curves.Add(s);
}
return p ;
}
I would suggest inheriting the ClassA and ClassB from a common base as you did and then casting them, after typechecking. If your only goal is minimizing code-repition this will get your there:
class Program
{
static void Main(string[] args)
{
ComponentClass[] c = new List<ComponentClass>().ToArray();//Data from 3rd party;
foreach (var data in c)
{
Parent p = new Parent();
if (data.classObjs.Count > 0)
{
Star s = new Star
{
Area = "infinite",
Color = "red"
};
foreach (var b in data.classObjs)
{
string bStr = b.GetType() == typeof(ClassA) ? ((ClassA)b).City : ((ClassB)b).Village;
bStr = bStr.Split(',').Select(string.Parse).ToList();
TinyStar t = new TinyStar
{
smallD = bStr
};
s.Values.Add(t);
}
p.Curves.Add(s);
}
}
}
public class ComponentClass
{
public List<ClassObj> classObjs { get; set; }
}
public class ClassObj
{
public int ID { get; set; }
public string Countries { get; set; }
}
public class ClassA : ClassObj
{
public string City { get; set; }
}
public class ClassB : ClassObj
{
public string Village { get; set; }
}
}
It might be necessary to refactor the tenary to if...else or switch depending on if you add more classes of type ClassObj.
Be warned though that GetType actually queries the assembly (at runtime) and should be used with caution from a performance point of view as it can slow down applications a lot when executed often.
I have used reflection, check if it works for you
public static void CommonMethod(dynamic collection)
{
Parent p = new Parent();
Star s = new Star();
s.Area = "infinite";
s.Color = "red";
foreach (var data in collection)
{
var properties = data.GetType().GetProperties();
foreach (var p in properties)
{
string propertytName = p.Name;
var propertyValue = p.GetValue(data, null);
if (propertytName == "City" || propertytName == "Village")
{
List<string> sep = propertyValue.Split(',').ToList();
foreach (var b in sep)
{
TinyStar t = new TinyStar();
t.smallD = b;
s.Values.Add(t);
}
p.Curves.Add(s);
}
}
}
}
static void Main(string[] args)
{
ComponentClass[] c Data from 3rd party;
foreach (var data in c)
{
CommonMethod(data.classAObj);
CommonMethod(data.classBObj);
}
}
I want to convert an object A to object B. The classes A and B have the same properties, just the names are changed.
I use this method:
/// <summary>
internal static T objectMapper<T>(object objectSource, T objectTarget)
{
dynamic o = objectSource;
Type typeA = objectSource.GetType();
Type typeB = objectTarget.GetType();
IList<PropertyInfo> propsA = new List<PropertyInfo>(typeA.GetProperties());
IList<PropertyInfo> propsB = new List<PropertyInfo>(typeB.GetProperties());
dynamic s;
ArrayList listArray = new ArrayList();
foreach (var prop in propsA)
{
s = objectSource.GetType().GetProperty(prop.Name).GetValue(objectSource, null);
listArray.Add(s);
}
int i = 0;
foreach (var prop in propsB)
{
prop.SetValue(objectTarget, listArray[i], null);
i++;
}
return objectTarget;
}
How can I edit properties of objectB in the foreach loop? I want to use a generic method for different objects.
This solution provides both your reflection-way and an alternative way by defining and implementing a copy method CopyFrom. To reduce code you could make the interface a base-class so you don't need to implement CopyFrom in the sub-classes....
public interface MyInterface
{
int Prop1 { get; set; }
string Prop2 { get; set; }
void CopyFrom(MyInterface obj);
}
public class A: MyInterface
{
public int Prop1 { get; set; }
public string Prop2 { get; set; }
public void CopyFrom(MyInterface obj)
{
this.Prop1 = obj.Prop1;
this.Prop2 = obj.Prop2;
}
}
public class B: MyInterface
{
public int Prop1 { get; set; }
public string Prop2 { get; set; }
public void CopyFrom(MyInterface obj)
{
this.Prop1 = obj.Prop1;
this.Prop2 = obj.Prop2;
}
}
public static class CopyUtils
{
public static void Copy(MyInterface src, MyInterface dst)
{
var props = typeof(MyInterface).GetProperties();
foreach(var prop in props)
{
prop.SetValue(dst, prop.GetValue(src, null), null);
}
}
}
I feel there might be a deeper architecture issue here. I'm failing to imagine why would you want to "copy" the values of the properties from one object of a class to another of a different class with the same property names.
If you're trying to "shape" the object maybe just passing an interface will do the work
Anyhow, see if this helps:
public static class ObjectMorpher
{
public class InvalidMorphException : Exception
{
}
[AttributeUsage(AttributeTargets.Property)]
public class IgnoredOnMorphAttribute : Attribute
{
}
public static TargetType Morph<TargetType>(this object source, TargetType dest, Func<string, string> propertyMatcher = null, bool failOnNoMatch = false)
where TargetType : class
{
if (source == null || dest == null)
throw new ArgumentNullException();
foreach (var sourceProp in source.GetType().GetProperties().Where(x => x.GetCustomAttributes<IgnoredOnMorphAttribute>().Any() == false))
{
var destProp = typeof(TargetType).GetProperties().Where(x => x.Name == ((propertyMatcher == null) ? sourceProp.Name : propertyMatcher(sourceProp.Name))).FirstOrDefault();
//check property exists
if (destProp == null)
{
if (failOnNoMatch)
throw new InvalidMorphException();
else
continue;
}
//check value type is assignable
if (!destProp.GetType().IsAssignableFrom(sourceProp.GetType()))
{
if (failOnNoMatch)
throw new InvalidMorphException();
else
continue;
}
destProp.SetValue(dest, sourceProp.GetValue(source));
}
return dest;
}
}
Usage example:
var A = new ClassA();
var B = new ClassB();
B = A.Morph(B);
Optionally you can set a property match for the case when properties doesn't have the exact same name.
Also notice the use of the IgnoredOnMorph attribute to mark properties as not morph-able (like calculated properties)
You might find automapper of use here (see https://github.com/AutoMapper/AutoMapper/wiki/Getting-started).
You would need to create a line for each object mapping in a startup file to set it up but if the properties are the same this would be as simple as:
mapper.CreateMap<ClassA, ClassB>().ReverseMap();
And then a single line to resolve the mapping when needed
mapper.Map(objectOfClassA, new ClassB());
I have Two Classes.
Class A:
public class A {
prop string p1 { get; set; }
prop string p2 { get; set; }
prop string p3 { get; set; }
}
Class B:
public class B {
prop string p1 { get; set; }
prop string p3 { get; set; }
}
Now assume we have an object from class B and we want to assign it to object from class A.
B b_obj = new B(){
p1 = "something",
p2 = "something else"
}
A a_obj = new A(){
p1 = b_obj.p1,
p3 = b_obj.p3,
}
I think the above solution is not best way.
What is best practice to assign b_obj to another object from class A?
Tip : All property in class B has a similar property in class A
You can always implement an implicit or explicit cast operator:
public class B
{
public static explicit operator A(B b)
{
return new A() {
p1 = b_obj.p1,
p3 = b_obj.p3,
}
}
//...
}
And now, you can simply write any time you need an A from a B:
var a = (A)b;
If you don't have access to either A or B then you could implement an extension method:
public static A ToA(this B b)
{
return ...
}
And the use would be similar:
var a = b.ToA();
You can use automapper
http://automapper.org/
Then you can use it like this:
AutoMapper.Mapper.CreateMap<A, B>();
var a = ...
var model = AutoMapper.Mapper.Map<B>(a);
You can use AutoMapper (see denisv's answer) which provides mappings between classes based on name. You can then customize your mappings if you want to.
You can also write some extension methods:
public static class Extensions
{
public A ToA(this B b)
{
return new A()
{
p1 = b_obj.p1,
p3 = b_obj.p3,
};
}
}
First of all initialize B class and set values. Then create such a class:
public static void CopyPropertyValues(object source, object destination)
{
var destProperties = destination.GetType().GetProperties();
foreach (var sourceProperty in source.GetType().GetProperties())
{
foreach (var destProperty in destProperties)
{
if (destProperty.Name == sourceProperty.Name &&
destProperty.PropertyType.IsAssignableFrom(sourceProperty.PropertyType))
{
destProperty.SetValue(destination, sourceProperty.GetValue(
source, new object[] { }), new object[] { });
break;
}
}
}
}
and pass A and B class to this class as Parameter
here is a simple example to clear my intentions.
class A {
public int Id
public string Name
public string Hash
public C c
}
class B {
public int id
public string name
public C c
}
class C {
public string name
}
var a = new A() { Id = 123, Name = "something", Hash = "somehash" };
var b = new B();
I want to set b's properties from a. I have tried something but no luck.
public void GenericClassMatcher(object firstModel, object secondModel)
{
if (firstModel != null || secondModel != null)
{
var firstModelType = firstModel.GetType();
var secondModelType = secondModel.GetType();
// to view model
foreach (PropertyInfo prop in firstModelType.GetProperties())
{
var firstModelPropName = prop.Name.ElementAt(0).ToString().ToLower(System.Globalization.CultureInfo.InvariantCulture) + prop.Name.Substring(1); // lowercase first letter
if (prop.PropertyType.FullName.EndsWith("Model"))
{
GenericClassMatcher(prop, secondModelType.GetProperty(firstModelPropName));
}
else
{
var firstModelPropValue = prop.GetValue(firstModel, null);
var secondModelProp = secondModelType.GetProperty(firstModelPropName);
if (prop.PropertyType.Name == "Guid")
{
firstModelPropValue = firstModelPropValue.ToString();
}
secondModelProp.SetValue(secondModel, firstModelPropValue, null);
}
}
}
}
What shall I do?
It sounds like you are trying to map one class to another. AutoMapper is the best tool I've come across to do this.
public class A
{
public int Id;
public string Name;
public string Hash;
public C c;
}
public class B
{
public int id;
public string name;
public C c;
}
public class C
{
public string name;
}
class Program
{
static void Main(string[] args)
{
var a = new A() { Id = 123, Name = "something", Hash = "somehash" };
var b = new B();
AutoMapper.Mapper.CreateMap<A, B>();
b = AutoMapper.Mapper.Map<A, B>(a);
Console.WriteLine(b.id);
Console.WriteLine(b.name);
Console.ReadLine();
}
}
I have a class
class TestFixture
{
public string a { get; set; }
public int b { get; set; }
public int c { get; set; }
public string d { get; set; }
public string e { get ; set ; }
public int f { get; set; }
public int g { get; set; }
public bool h { get; set; }
public string i { get; set; }
public bool j { get; set; }
public bool k { get; set; }
public TestFixture()
{
e= dosomething(a, b);
f= false;
g = DateTime.Now.ToString("yyMMddhhmmss");
h= TestName.Equals("1") && b.Equals("2") ? 1000 : 1;
i= 10000000;
j= a.Equals("FOT");
k = false;
}
}
I want to define new TestFixture as SO
new TestFixture { a = "", b = 1, c=2, d="" };
while the rest of properties should be auto defined as it written in constructor.
Is it possible ?
Yes, this is possible. Using an object initializer does not skip calling the constructor.
TestFixture fixture = new TestFixture() // or just new TestFixture { ... }
{
a = "",
b = 1,
c = 2,
d = ""
};
This will call the constructor you've defined and then set a, b, c, and d in your object initializer.
Pop a breakpoint in your constructor and run your debugger. This is should show you how and when things in your code are called.
Debugging in Visual Studio
Refactored:
public class TestFixture
{
public string a { get; set; }
public int b { get; set; }
public int c { get; set; }
public string d { get; set; }
// dosomething should check for null strings
public string e { get { return dosomething(a, b); } }
public int f { get; set; }
public int g { get; set; }
public bool h
{
get { return TestName.Equals("1") && b.Equals("2") ? 1000 : 1; }
}
public string i { get; set; }
public bool j { get { return a != null && a.Equals("FOT"); } }
public bool k { get; set; }
public TestFixture(string a, int b, int c, string d)
: this()
{
this.a = a;
this.b = b;
this.c = c;
this.d = d;
}
public TestFixture()
{
f = false;
g = DateTime.Now.ToString("yyMMddhhmmss");
i = 10000000;
k = false;
}
}
#hunter's answer is correct, you can use object initializer syntax, and those properties will be set after your constructor runs. However, I'd like to point out some flaws you may have with your code
public TestFixture()
{
e= dosomething(a, b);
f= false;
g = DateTime.Now.ToString("yyMMddhhmmss");
h= TestName.Equals("1") && b.Equals("2") ? 1000 : 1;
i= 10000000;
j= a.Equals("FOT");
k = false;
}
This code does not set a or b, but you have things that depend on their values (e, g, j). Object initializer syntax is not going to be useful here, you have to have proper defaults for these values if other values in the constructor will depend upon them.
As an example, when you write var obj = new Bar() { A = "foo" };, that will expand to
var obj = new Bar(); // constructor runs
obj.A = "Foo"; // a is set
Clearly, the code in the constructor that looks at A will not see the value "Foo". If you need it to see this value, object initialization strategy is not going to help. You need a constructor overload that takes the value to be stored in A.
var obj = new Bar("Foo");
If I understand you right, you would like to the a, b, c and d properties to be initialized with the given values before the constructor runs. Unfortunately, that is not possible this way, because the default constructor always runs before the object intializers.
I advise you to do something like this instead:
class TestFixture
{
//... properties
public TestFixture()
{
this.init();
}
public TestFixture(string a, int b, int c, string d)
{
this.a = a;
this.b = b;
this.c = c;
this.d = d;
this.init();
}
private void init()
{
e= dosomething(a, b);
f= false;
g = DateTime.Now.ToString("yyMMddhhmmss");
h= TestName.Equals("1") && b.Equals("2") ? 1000 : 1;
i= 10000000;
j= a.Equals("FOT");
k = false;
}
}
This way you can init the a, b, c and d properties before the other initializer code runs.