Considering the following scheme.
A class that contains an object, in this case I'm using OriginObject.
The main problem is that the property NestedName has to access instructions about its behavior from another property within the OriginObject, in that case I thought about using reflection but, it's getting really confused.
The problem:
public class OriginObject
{
public NestedObject nestedObject { get; set; }
public Instructions Instruct { get; set; }
public class NestedObject
{
public string NestedName { get; set; }
public void GetName()
{
NestedName = //this.instruct.Name? being "this" the OriginObject;
}
}
public struct Instructions
{
public string Name { get; set; }
}
}
public void GetName()
{
NestedName = GetType().DeclaringType
.GetProperty("instruct")
.GetValue(GetType().DeclaringType)
.GetType()
.GetProperty("Name")
.GetValue(wtf im doing);
}
I know I can simply add a parameter that provides me the Origin Object but, in my logical base knowledge this means Clean and beautiful code:
/Target.DoSomething( );/
or in production terms speaking it will be something like
Body.Parse( );
Does somebody know any other way to do this? Without creating an ilegal crime of sequential methods?
If it's just a bad idea I can change it.
I understand that this is some code that you're just playing around with and that you're not really looking at having three classes to encapsulate one string...
With your code as it is, an instance of OriginObject.NestedObject does not hold onto a reference to an instance of OriginObject.
Having your classes nested is nice if you want to be able to access nonpublic members (methods, etc.) but everything in your code is public so there's no benefit there.
As there's no coupling (OriginObject.NestedObject and OriginObject.Instructions don't access any methods or static members on OriginObject) your code is equivalent to how they would be if there was no nesting.
You can probably see why your code won't work if you rewrite NestedObject and Instructions to not nested - you'd have this equivalent code:
public class OriginObject
{
public NestedObject nestedObject { get; set; }
public Instructions Instruct { get; set; }
}
public class NestedObject
{
public string NestedName { get; set; }
public void GetName()
{
NestedName = // How is NestedObject supposed to get anything from OriginObject?
}
}
public struct Instructions
{
public string Name { get; set; }
}
One fix would be to pass in an instance of OriginObject to a constructor of NestedObject, e.g.
public class OriginObject
{
public NestedObject Nested { get; set; }
public Instructions Instruct { get; set; }
public class NestedObject
{
private OriginObject _origin;
public NestedObject(OriginObject origin)
{
_origin = origin;
}
public string NestedName
{
get; set;
}
public void GetName()
{
NestedName = _origin.Instruct.Name;
}
}
public struct Instructions
{
public string Name { get; set; }
}
}
You'd then do something like this:
var instructions = new OriginObject.Instructions() { Name = "Test" };
var origin = new OriginObject() { Instruct = instructions };
var nested = new OriginObject.NestedObject(origin);
nested.GetName();
That was a bit clunky though - you have to pass in the OriginObject to the OriginObject.NestedObject, but you only ever use it to access the Instructions property. It'd be better if you just passed the Instructions object directly into the constructor, e.g.:
public class OriginObject
{
public NestedObject Nested { get; set; }
public Instructions Instruct { get; set; }
public class NestedObject
{
private Instructions _instructions;
public NestedObject(Instructions instructions)
{
_instructions = instructions;
}
public string NestedName
{
get; set;
}
public void GetName()
{
NestedName = _instructions.Name;
}
}
public struct Instructions
{
public string Name { get; set; }
}
}
which you'd use with something like this:
var instructions = new OriginObject.Instructions() { Name = "Test" };
var origin = new OriginObject() { Instruct = instructions };
var nested = new OriginObject.NestedObject(instructions);
nested.GetName();
Now, it's a bit weird to have a GetName() method that you have to call each time and doesn't return anything. Better still might be to get rid of the GetName() method and have the NestedName property a computed property instead:
public class OriginObject
{
public NestedObject Nested { get; set; }
public Instructions Instruct { get; set; }
public class NestedObject
{
private Instructions _instructions;
public NestedObject(Instructions instructions)
{
_instructions = instructions;
}
public string NestedName
{
get
{
return _instructions.Name;
}
}
}
public struct Instructions
{
public string Name { get; set; }
}
}
You'd then call that with an arguably simpler bit of code:
var instructions = new OriginObject.Instructions() { Name = "Test" };
var origin = new OriginObject() { Instruct = instructions };
var nested = new OriginObject.NestedObject(instructions);
At this point you might as well have a constructor for OriginObject too, so you know that the Instructions and the NestedObject instances are set - you could have something like this:
public class OriginObject
{
public NestedObject Nested { get; set; }
public Instructions Instruct { get; set; }
public OriginObject(NestedObject nested, Instructions instruct)
{
Nested = nested;
Instruct = instruct;
}
public class NestedObject
{
private Instructions _instructions;
public NestedObject(Instructions instructions)
{
_instructions = instructions;
}
public string NestedName
{
get
{
return _instructions.Name;
}
}
}
public struct Instructions
{
public string Name { get; set; }
}
}
which you'd use with some code like this:
var instructions = new OriginObject.Instructions() { Name = "Test" };
var nested = new OriginObject.NestedObject(instructions);
var origin = new OriginObject(nested, instructions);
Note that there's still no need for any nested types: you could have this equivalent code without nesting:
public class OriginObject
{
public NestedObject Nested { get; set; }
public Instructions Instruct { get; set; }
public OriginObject(NestedObject nested, Instructions instruct)
{
Nested = nested;
Instruct = instruct;
}
}
public class NestedObject
{
private Instructions _instructions;
public NestedObject(Instructions instructions)
{
_instructions = instructions;
}
public string NestedName
{
get
{
return _instructions.Name;
}
}
}
public struct Instructions
{
public string Name { get; set; }
}
which would be called with code like the following:
var instructions = new Instructions() { Name = "Test" };
var nested = new NestedObject(instructions);
var origin = new OriginObject(nested, instructions);
Related
I have a Json class "GetAllDevices()". My JSON response consists of an Array/List of objects, where each object has the below common properties.
public class GetAllDevices
{
[JsonProperty("_id")]
public string Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("actions")]
public Action[] Actions { get; set; }
public class Action
{
public string _id { get; set; }
public Action_Def action_def { get; set; }
}
public class Action_Def
{
public string _id { get; set; }
public string name { get; set; }
}
}
I want to create 2 generic lists containing all the above properties based on its "type".
lstfoo1 List contains all the properties(_id, name type and actions) where type="foo1". Similarly, lstfoo2 is a List which contains the above properties where type="foo2".
What I have done so far:
string strJson=getJSON();
Foo1 lstfoo1=new Foo1();
Foo2 lstfoo2=new Foo2();
List<Foo1> foo1list= lstfoo1.GetDeviceData(strJson);
List<Foo2> foo2list = lstfoo2.GetDeviceData(strJson);
public class AllFoo1: GetAllDevices
{
}
public class AllFoo2: GetAllDevices
{
}
public abstract class HomeDevices<T>
{
public string type { get; set; }
public string _id { get; set; }
public List<AllFoo1> lstfoo1{ get; set; }
public List<AllFoo2> lstfoo2{ get; set; }
public abstract List<T> GetDeviceData(string jsonResult);
}
public class Foo1: HomeDevices<AllFoo1>
{
public Foo1()
{
type = "foo1";
}
public override List<AllFoo1> GetDeviceData(string jsonResult)
{
var lst =Newtonsoft.Json.JsonConvert.DeserializeObject<List<AllFoo1>>(jsonResult);
var lst1 = lst.Where(x => x.Type.Equals(type)).ToList();
return lst1;
}
}
public class Foo2: HomeDevices<AllFoo2>
{
public Foo2()
{
type = "foo2";
}
public override List<AllFoo2> GetDeviceData(string jsonResult)
{
var lst = Newtonsoft.Json.JsonConvert.DeserializeObject<List<AllFoo2>>(jsonResult);
var lst1 = lst.Where(x => x.Type.Equals(type)).ToList();
return lst1;
}
}
My question is, is there an easier way to do this using abstract classes? Can I directly convert my "GetAllDevices" class into an abstract class and inherit it and deserialize into it and create a generic list?
This should help, if I understand your problem correctly. Let me know if you have questions or it doesn't work as you need. I put this together really quickly without testing.
The way the Type property is defined could be improved but I left it as you had it.
public class MyApplication
{
public void DoWork()
{
string json = getJSON();
DeviceTypeOne foo1 = new DeviceTypeOne();
DeviceTypeTwo foo2 = new DeviceTypeTwo();
IList<DeviceTypeOne> foo1Results = foo1.GetDeviceData(json); // calls GetDeviceData extension method
IList<DeviceTypeTwo> foo2Results = foo2.GetDeviceData(json); // calls GetDeviceData extension method
}
}
// implemented GetDeviceData as extension method of DeviceBase, instead of the abstract method within DeviceBase,
// it's slightly cleaner than the abstract method
public static class DeviceExtensions
{
public static IList<T> GetDeviceData<T>(this T device, string jsonResult) where T : DeviceBase
{
IEnumerable<T> deviceDataList = JsonConvert.DeserializeObject<IEnumerable<T>>(jsonResult);
IEnumerable<T> resultList = deviceDataList.Where(x => x.Type.Equals(typeof(T).Name));
return resultList.ToList();
}
}
// abstract base class only used to house common properties and control Type assignment
public abstract class DeviceBase : IDeviceData
{
protected DeviceBase(string type)
{
if(string.IsNullOrEmpty(type)) { throw new ArgumentNullException(nameof(type));}
Type = type; // type's value can only be set by classes that inherit and must be set at construction time
}
[JsonProperty("_id")]
public string Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("type")]
public string Type { get; private set;}
[JsonProperty("actions")]
public DeviceAction[] Actions { get; set; }
}
public class DeviceTypeOne : DeviceBase
{
public DeviceTypeOne() : base(nameof(DeviceTypeOne))
{
}
}
public class DeviceTypeTwo : DeviceBase
{
public DeviceTypeTwo() : base(nameof(DeviceTypeTwo))
{
}
}
// implemented GetAllDevices class as IDeviceData interface
public interface IDeviceData
{
string Id { get; set; }
string Name { get; set; }
string Type { get; }
DeviceAction[] Actions { get; set; }
}
// renamed and relocated class Action to DeviceAction
public class DeviceAction
{
public string Id { get; set; }
public DeviceActionDefinition DeviceActionDefinition { get; set; }
}
// renamed and relocated Action_Def to DeviceActionDefinition
public class DeviceActionDefinition
{
public string Id { get; set; }
public string Name { get; set; }
}
It should be simple enough to move the implementation of method GetDeviceData() to the base class.
For this to work, you will need to add a constraint on T so the compiler knows a bit more about the base type. You will also need to implement a constructor to populate the concrete type's type string you use around. This is a necessary measure to ensure the value is always populated as it is used for comparison in the method in question:
public abstract class HomeDevices<T> where T: GetAllDevices
{
public HomeDevices(string concreteType)
{
type = concreteType;
}
public string type { get; set; }
public string _id { get; set; }
public List<AllFoo1> lstfoo1 { get; set; }
public List<AllFoo2> lstfoo2 { get; set; }
//This method is now generic and works for both.
public List<T> GetDeviceData(string jsonResult)
{
var lst = Newtonsoft.Json.JsonConvert.DeserializeObject<List<T>>(jsonResult);
var lst1 = lst.Where(x => x.Type.Equals(type)).ToList();
return lst1;
}
}
I hope that helps.
I'm at loss here. I want to refactor a part of the code that uses no abstract classes. I'm familiar with json2csharp. That converts a JSON file to the C# classes so it can be easily deserialized.
Is there a similar site/tool that accepts as input several C# classes and generates basic abstract classes based on those?
This would make the refactoring easier as I don't need to create all the different abstract classes.
Very simple example:
Input:
public class TestClass1
{
public string TestID { get; set; }
public string TestName { get; set; }
public int TestValue1 { get; set; }
public TestClass1()
{
}
}
public class TestClass2
{
public string TestID { get; set; }
public string TestName { get; set; }
public int TestValue2 { get; set; }
public TestClass2()
{
}
}
Output:
public abstract class ATestClass
{
public string TestID { get; set; }
public string TestName { get; set; }
protected ATestClass()
{
}
}
You can get something working pretty quickly if you use the Roslyn code analysis and code generation. Here’s a quick example how that could work. Note that this is somewhat fragile with detecting common properties since its based on the syntax instead of the actual semantics (making string Foo and String Foo incompatible properties). But for code that is actually generated by another code generator, this should work fine since the input should be consistent.
var input = #"
public class TestClass1
{
public string TestID { get; set; }
public string TestName { get; set; }
public string OtherTest { get; set; }
public int TestValue1 { get; set; }
public TestClass1()
{
}
}
public class TestClass2
{
public string TestID { get; set; }
public string TestName { get; set; }
public int OtherTest { get; set; }
public int TestValue2 { get; set; }
public TestClass2()
{
}
}";
// parse input
var tree = CSharpSyntaxTree.ParseText(input);
// find class declarations and look up properties
var classes = tree.GetCompilationUnitRoot().ChildNodes()
.OfType<ClassDeclarationSyntax>()
.Select(cls => (declaration: cls, properties: cls.ChildNodes().OfType<PropertyDeclarationSyntax>().ToDictionary(pd => pd.Identifier.ValueText)))
.ToList();
// find common property names
var propertySets = classes.Select(x => new HashSet<string>(x.properties.Keys));
var commonPropertyNames = propertySets.First();
foreach (var propertySet in propertySets.Skip(1))
{
commonPropertyNames.IntersectWith(propertySet);
}
// verify that the property declarations are equivalent
var commonProperties = commonPropertyNames
.Select(name => (name, prop: classes[0].properties[name]))
.Where(cp =>
{
foreach (var cls in classes)
{
// this is not really a good way since this just syntactically compares the values
if (!cls.properties[cp.name].IsEquivalentTo(cp.prop))
return false;
}
return true;
}).ToList();
// start code generation
var workspace = new AdhocWorkspace();
var syntaxGenerator = SyntaxGenerator.GetGenerator(workspace, LanguageNames.CSharp);
// create base class with common properties
var baseClassDeclaration = syntaxGenerator.ClassDeclaration("BaseClass",
accessibility: Accessibility.Public,
modifiers: DeclarationModifiers.Abstract,
members: commonProperties.Select(cp => cp.prop));
var declarations = new List<SyntaxNode> { baseClassDeclaration };
// adjust input class declarations
commonPropertyNames = new HashSet<string>(commonProperties.Select(cp => cp.name));
foreach (var cls in classes)
{
var propertiesToRemove = cls.properties.Where(prop => commonPropertyNames.Contains(prop.Key)).Select(prop => prop.Value);
var declaration = cls.declaration.RemoveNodes(propertiesToRemove, SyntaxRemoveOptions.KeepNoTrivia);
declarations.Add(syntaxGenerator.AddBaseType(declaration, syntaxGenerator.IdentifierName("BaseClass")));
}
// create output
var compilationUnit = syntaxGenerator.CompilationUnit(declarations);
using (var writer = new StringWriter())
{
compilationUnit.NormalizeWhitespace().WriteTo(writer);
Console.WriteLine(writer.ToString());
}
This would generate the following output:
public abstract class BaseClass
{
public string TestID
{
get;
set;
}
public string TestName
{
get;
set;
}
}
public class TestClass1 : BaseClass
{
public string OtherTest
{
get;
set;
}
public int TestValue1
{
get;
set;
}
public TestClass1()
{
}
}
public class TestClass2 : BaseClass
{
public int OtherTest
{
get;
set;
}
public int TestValue2
{
get;
set;
}
public TestClass2()
{
}
}
I have the following problem-
I'm trying to serialize a class which contains a class that has an additional dictionary.
The structure is simplified to the following:
public class GroupVM
{
public GroupVM()
{
this.Clusters = new Dictionary<int, ClusterVM>();
}
public Dictionary<int,ClusterVM> Clusters { get; set; }
}
public class ClusterVM
{
public ClusterVM()
{
this.Attributes = new Dictionary<Guid, AttributeVM>();
}
Dictionary<Guid,AttributeVM> Attributes { get; set; }
public void AddAttribute(Guid guid, string name)
{
AttributeVM attrVM = new AttributeVM();
attrVM.Name = name;
attrVM.Guid = guid;
this.Attributes.Add(guid,attrVM);
}
}
public class AttributeVM
{
public Guid Guid { get; set; }
public string Name { get; set; }
}
I'm trying to use it in API, and return a serialized version of GroupVM. For some reason, I'm getting nothing in the Attributes Dictionary (inside ClusterVM class).
If I change it to List, it works fine.
Code Sample
According to the sample code the Attributes property was not public
Dictionary<Guid,AttributeVM> Attributes { get; set; }
It could not get serialized because the serializer was unaware of its presence. Make the property public and it should get serialized.
public class ClusterVM {
public ClusterVM() {
this.Attributes = new Dictionary<Guid, AttributeVM>();
}
public IDictionary<Guid,AttributeVM> Attributes { get; set; }
public void AddAttribute(Guid guid, string name) {
AttributeVM attrVM = new AttributeVM();
attrVM.Name = name;
attrVM.Guid = guid;
this.Attributes.Add(guid,attrVM);
}
}
I'm having a problem defining these 2 classes:
public class Article
{
public Article(long ID, string Name, ArticleFamily Family)
{
//...Initializer...
}
public ArticleFamily Family { get; set; }
//Other props...
}
public class ArticleFamily
{
public ArticleFamily(int ID, string Description)
{
//...Initializer...
}
public int ID { get; private set; }
public string Description { get; set; }
}
I have a collection of Article and each one belongs to a family.
Now, given that I have a certain ArticleFamily object I should be able to change its Description and it gets eventually persisted to a DataBase. (I left out that part for simplicity)
But I should not be able to do this:
Article art = SomeMethodReturningArticle();
art.Family.Description = "SomeOtherValue";
I should be able to change the Family of an Article entirely, replacing it with a new ArticleFamily object, but I shouldn't be able to change just the description.
Should I create a copy of the ArticleFamily class with readonly properties like this:
public class ArticleFamilyReadonly
{
ArticleFamily _family;
public ArticleFamilyReadonly(ArticleFamily Family)
{
_family = Family;
}
public int ID { get { return _family.ID; } }
//etc...
}
How can I do this in a clean way?
Here's what I threw together in LinqPad:
void Main()
{
var art = new Article(1,"2", new ArticleFamily(1, "Test"));
art.Family.Description = "What?"; // Won't work
var fam = art.Family as ArticleFamily;
fam.Description = "This works"; // This works...
}
public class Article
{
public Article(long ID, string Name, IArticleFamily Family)
{
//...Initializer...
}
public IArticleFamily Family { get; set; }
//Other props...
}
public class ArticleFamily : IArticleFamily
{
public ArticleFamily(int ID, string Description)
{
//...Initializer...
}
public int ID { get; private set; }
public string Description { get; set; }
}
public interface IArticleFamily
{
int ID { get; }
string Description { get;}
}
Cannot edit directly from the Article object unless cast to ArticleFamily object.
I have two models.
Source model:
public sealed class adresse
{
public string strasse { get; set; }
public string hausnummer { get; set; }
public string plz { get; set; }
public string ort { get; set; }
public string landCode { get; set; }
}
Destination model:
public sealed class Adresse
{
public string Strasse { get; set; }
public string Hausnummer { get; set; }
public string Plz { get; set; }
public string Ort { get; set; }
public string LandCode { get; set; }
}
Therefore I created a mapping with automapper and a unit test.
public class AddressMapper
{
public Address map()
{
adresse add = new adresse();
add.hausnummer = "1";
add.ort = "Test";
AutoMapper.Mapper.Initialize(cfg => {
cfg.AddProfile<Profile1>();
});
return AutoMapper.Mapper.Map<Address>(add);
}
}
public class LowerNamingConvention : INamingConvention
{
public Regex SplittingExpression
{
get { return new Regex(#"[\p{Ll}a-z A-Z 0-9]+(?=_?)"); }
}
public string SeparatorCharacter
{
get { return string.Empty; }
}
}
public class Profile1 : Profile
{
protected override void Configure()
{
SourceMemberNamingConvention = new LowerNamingConvention();
DestinationMemberNamingConvention = new PascalCaseNamingConvention();
CreateMap<adresse, Address>();
}
}
[TestFixture]
public class AddressMapperTest
{
[Test]
public void TestMapper()
{
var sut = new AddressMapper();
var value = sut.map();
}
}
When I'm running the test every field in the destination model is null.
As you can see there is a problem with the naming because some names in the source model I have some times different naming conventions like lower case or lower camel case. Does anyone have an idea to solve this problem? Or do I have to map everything manualy?
You should use DataContract and DataMember attributes as below, so that you don't need to give property a same name and also you can follow coding standards.
[DataContract(Namespace = "")]
public class YourClass
{
[DataMember(EmitDefaultValue = false, Name = "myVariable")]
public string MyVariable { get; set; }
}
I think i found a proper solution for my problem. Sorry for not being familar with Regex. I just combined the RegExes from
AutoMapper/src/AutoMapper/PascalCaseNamingConvention.cs
and
AutoMapper/src/AutoMapper/LowerUnderscoreNamingConvention.cs
into my own naming convention. Might be that there are cases wich can cause issues. But as far as I have tested it works.
public class LowerNamingConvention : INamingConvention
{
public Regex SplittingExpression
{
get { return new Regex(#"[\p{Ll}0-9]+(?=$|\p{Lu}[\p{Ll}0-9])|\p{Lu}?[\p{Ll}0-9]+)"); }
}
public string SeparatorCharacter
{
get { return string.Empty; }
}
}