Ok so, I've run into an interested and probably simple problem. I have a base class that is inherited by another class (child). I have the same parameterless constructor in the base and the child. I would like to set defaults in the child that propagate into the base properties. I would like to do something like this:
public partial class baseclass
{
public baseclass() {
//never called if instantiated from baseclass(string newp1)
p1 = "";
p2 = "google";
}
public baseclass(string newp1) {
p1 = newp1; //p2 will be "" and p1 will be newP1
}
public string p1 { get; set; }
public string p2 { get; set; }
}
public partial class childclass : baseclass
{
public childclass() {
//How can I call this to set some default values for the child?
p2 = "facebook";
}
public childclass(string newp1) : base(newp1) {
p1 = newp1; //p2 needs to be "facebook"
}
}
Use constructors chaining if you have duplicated code in several constructors:
public class baseclass
{
public baseclass() : this("google") { }
public baseclass(string newp1)
{
p1 = newp1; // the only place in your code where you init properties
p2 = "";
}
public string p1 { get; set; }
public string p2 { get; set; }
}
Child class should inherit baseClass
public class childclass : baseclass
{
public childclass() : this("facebook") { } // you can also call base here
public childclass(string newp1) : base(newp1) { }
}
Also keep in mind that parital just allows you split class/method definiton in several parts (e.g. keep it in different files). It is useful when you are generating classes (e.g. from database tables) but still want to add/customize something in generated classes. If you will put customized code directly into generated files, then it will be lost after classes re-generation. Read more
You can create a protected constructor in base class and call it in child class:
public class Base
{
public Base(int value1, int value2) { ... }
protected Base(string value1) { ... } // only for child class
}
public class Child : Base
{
public Child() : Base("Value") { ... }
}
Related
Explanation
I use the xsd.exe tool to generate a class API based upon an XSD (defines a schema for XML files) from which to interact with XML files.
This tool works well, but this issue is that I have a few schemas which are by-and-large identical with small tweaks, so I want to create interfaces or abstract classes that allow me to reuse code elsewhere.
In my example below I've simplified the generated code for purposes of sharing it here but the principle still holds.
Example Non-Functioning Code
Program.cs
public static void Main()
{
BaseColor baseColor = new Color1 { ColorDescription = "Red" };
BaseShape baseShape = new Shape1 { Content = baseColor };
}
Models.cs
//Auto generated models - I have no control over these but they are partial classes
//First set of autogenerated models, normally in its own file
public partial class Shape1
{
public Color1 Content { get; set; }
}
public partial class Color1
{
public string ColorDescription { get; set; }
}
//Second set of autogenerated models, normally in its own file
public partial class Shape2
{
public Color2 Content { get; set; }
}
public partial class Color2
{
public string ColorDescription { get; set; }
}
//Attemping to abstract these classes so I can generically use them regardless of underlying type
public abstract class BaseShape
{
public abstract BaseColor Content { get; set; }
}
public abstract class BaseColor
{
public abstract string ColorDescription { get; set; }
}
//Attempting to extend the autogenerated classes with the abstract classes
public partial class Shape1 : BaseShape { }
public partial class Color1 : BaseColor { }
public partial class Shape2 : BaseShape { }
public partial class Color2 : BaseColor { }
Errors
This error is repeated 8 times in total for both shapes, both colors and both get/set methods.
'Shape1' does not implement inherited abstract member 'BaseShape.Content.set' XmlSerializeChild
And from the Main method.
Cannot implicitly convert type 'XmlSerializeChild.Models.BaseColor' to 'XmlSerializeChild.Models.Color1'. An explicit conversion exists (are you missing a cast?)
You where quite close, but as TheGeneral wrote in his comment, you can't change the type of the property when overriding it.
What you can do is introduce a new property (I've chosen to use interfaces but it will work just as well with abstract classes) that will be used in the code, having the explicit cast in each partial class:
So first, I've created the interfaces:
public interface IColor { string ColorDescription { get; set; } }
public interface IShape { IColor BaseContent { get; set; } }
Then, added the IColor implementation to the Color1 and Color2 classes:
public partial class Color1 : IColor {}
public partial class Color2 : IColor {}
(That was the easy part since the ColorDescription is the same type for both colors).
Next, I've added the IShape implementation to the Shape1 and Shape2 classes:
public partial class Shape1 : IShape
{
public IColor BaseContent
{
get { return Content; }
set { Content = (Color1) value; }
}
}
public partial class Shape2 : IShape
{
public IColor BaseContent
{
get { return Content; }
set { Content = (Color2) value; }
}
}
Now, in the Main method, you can do this:
var baseColor = new Color1() { ColorDescription = "Red" };
var baseShape = new Shape1() { BaseContent = baseColor };
Another option instead of introducing a new property would be to implicitly implement the IShape interface - but this would be more cumbersome and will not allow you to use the new Shape1() {Content = baseColor} syntax. Still, let's review this option as well:
So we rename the BaseContent property in the IShape interface:
interface IShape { IColor Content { get; set; } }
And we implement it like this:
public partial class Shape1 : IShape
{
IColor IShape.Content
{
get { return ((Shape1)this).Content; }
set { ((Shape1)this).Content = (Color1) value; }
}
}
public partial class Shape2 : IShape
{
IColor IShape.Content
{
get { return ((Shape2)this).Content; }
set { ((Shape2)this).Content = (Color2) value; }
}
}
Then, we create our reverences like this:
var baseColor = new Color1() { ColorDescription = "Red" };
// Note: Do not use var here - you need the reference to be of type `IShape`!
IShape baseShape = new Shape1();
baseShape.Content = baseColor;
Is there a way i can have derived classes override the default value of the base class? In the example below i would need the Hammer.Name to return "Hammer".
public class ItemBase
{
public string Name = "Base";
}
public class Hammer: ItemBase
{
new public string Name = "Hammer";
}
public class Test
{
ItemBase MyThing = new Hammer();
// Prints "Base"
Console.WriteLine(ItemBase.Name);
}
You don't need different fields, you need different initializations of the same field.
class Base {
protected string name = "";
public Base() { name = "X"};
}
class Derived : Base {
public Derived() { name = "Y"}; //same {name } field of a Base class
}
You might consider using virtual properties instead of exposing public fields (which is considered bad practice).
As such, you can (with C# 6.0):
void Main()
{
ItemBase myThing = new Hammer();
// Doesn't print "Base"
Console.WriteLine(myThing.Name);
}
public class ItemBase
{
public virtual string Name { get; } = "Base";
}
public class Hammer : ItemBase
{
public override string Name { get; } = "Hammer";
}
or (if you're using older version of C#)...
public class ItemBase
{
public virtual string Name { get { return "Base"; } }
}
public class Hammer : ItemBase
{
public override string Name { get { return "Hammer"; } }
}
You are not defining a new default value in the derived type, you are declaring a completely new field that hides the field with the same name in the base class.
Because fields can't be virtual, the returned field is the one declared in the type through which you are invoking it.
Solution? Don't redeclare a new field, simply assign a new value to the existing field in the constructor of the derived type:
public class Hammer
{
public Hammer() {
Name = "Hammer"; }
}
Trying to figure out what exactly is needed while skating around the .NET version restrictions has been a headache but I have a solution. According to your comments you can use a constructor.
In that case this is really easy to do with properties (which are the preferred way to handle your situation) instead of public fields:
public class ItemBase
{
public ItemBase()
{
//When instantiating ItemBase the value of Name is "Base"
Name = "Base";
}
public string Name { get; set; }
}
public class Hammer : ItemBase
{
public Hammer()
{
//When instantiating Hammer the value of Name is "Hammer"
Name = "Hammer";
}
}
And to test just run this:
public class Program
{
public static void Main()
{
ItemBase itemBase = new Hammer();
Console.WriteLine(itemBase.Name);
itemBase.Name = "Foo";
Console.WriteLine(itemBase.Name);
}
}
Outputs:
Hammer
Foo
This should check off all the boxes. You now use properties (making your code better), each class has a default value, and the properties can be changed after instantiation.
Let me describe the logic and then class structure. There are objects and all object must inherit from ConfigurationObjectBase. Each object must be owned by Manager and all Managers must be derived from ConfigurationObjectManagerBase. When a new instance of object created, one of the constructor must accept instance of Manager and that instance of Manager must add that instance of object into it's property called ChildObjects. Below is sample of classes. could you pls help to correct in below code acording above business rule? Thanks.
public class ConfigurationObjectBase<ObjectType>
{
public ConfigurationObjectBase(ConfigurationObjectManagerBase<ObjectType> ownerManager)
{
ownerManager.ChildObjects.Add(this);
}
}
public class ConfigurationObjectManagerBase<ObjectType>
{
public ConfigurationObjectManagerBase()
{
ChildObjects = new List<ObjectType>();
}
public List<ObjectType> ChildObjects { get; set; }
}
public class Catalog : ConfigurationObjectBase<Catalog>
{
public Catalog(CatalogManager ownerManager) : base(???)
{
}
}
public class CatalogManager : ConfigurationObjectManagerBase<CatalogManager>
{
public CatalogManager() : base()
{
}
}
There are two issues in your code:
CatalogManager should inherit from ConfigurationObjectManagerBase<Catalog>, not ConfigurationObjectManagerBase<CatalogManager>
ChildObjects should probably be a list of ConfigurationObjectBase<ObjectType>, rather than a list of ObjectType (otherwise you can't add a ConfigurationObjectBase<ObjectType> to it)
So the code should probably look like this:
public class ConfigurationObjectBase<ObjectType>
{
public ConfigurationObjectBase(ConfigurationObjectManagerBase<ObjectType> ownerManager)
{
ownerManager.ChildObjects.Add(this);
}
}
public class ConfigurationObjectManagerBase<ObjectType>
{
public ConfigurationObjectManagerBase()
{
ChildObjects = new List<ConfigurationObjectBase<ObjectType>>();
}
public List<ConfigurationObjectBase<ObjectType>> ChildObjects { get; set; }
}
public class Catalog : ConfigurationObjectBase<Catalog>
{
public Catalog(CatalogManager ownerManager) : base(ownerManager)
{
}
}
public class CatalogManager : ConfigurationObjectManagerBase<Catalog>
{
public CatalogManager()
{
}
}
Also, you don't need to call the default base class constructor (base()), it's done implicitly by the compiler.
public interface IHasFeature<TFeature> {
TFeature Feature { get; set; }
}
public class FeatureOne {
/*...*/
}
public class ProductOne : IHasFeature<FeatureOne> {
public FeatureOne Feature { get; set; }
}
public abstract class BaseContainer<TProduct, TFeature>
where TProduct : IHasFeature<TFeature> {
public TProduct Product { get; set; }
public void DoProcess() {
var result = Product.Feature.Execute(); //Execute is an extension method
}
}
public class MyContainer : BaseContainer<ProductOne, FeatureOne> {
/*...*/
}
Works when I do:
MyContainer : BaseContainer<ProductOne, FeatureOne>
But I want to:
MyContainer : BaseContainer<ProductOne>
ProductOne : IHasFeature<...> should already contain the nested generic feature TFeature, I don't want to repeat them again in MyContainer construction.
Any idea how I can improve this? Thanks.
EDIT2 -----------------------
Removed new keyword, it was wrong as Nenad said.
The compile translates your call to the extension method Execute() as a call to the static method of the class where it is defined. So, when you call Product.Feature.Execute() inside DoProcess(), the compiler needs to know the type of Product.Feature in order to call the appropriate extension method.
What I suggest is write IHasFeature like this
public interface IHasFeature
{
void DoSomethingWithFeature();
}
If you want to keep IHasFeature generic, the best you can do is define BaseContainer without specifying TFeature and implement DoProcess() in MyContainer.
public abstract class BaseContainer<TProduct>
{
public TProduct Product { get; set; }
public abstract void DoProcess();
}
public class MyContainer : BaseContainer<ProductOne>
{
public override void DoProcess()
{
Product.Feature.Execute();
}
}
I have a base class that implements a number of properties I want to store, and a derived class that contains additional properties that I don't want to store (they can be quite large). I have a container class that contains a list of the base class, which in turn contains a mixture of instances of the base class and its derived class. I create an XMLSerializer for the container class, but on serialization it complains that the derived class isn't included.
Is there any way of forcing the serializer to output base class XML only, irrespective of the instance type?
Note, I don't want to use XMLInclude, as I specifically don't want any of the properties in the derived class to be stored.
(Simplified example of the code)
public class MyBase {
public String Title { get; set; }
}
public class MyDerived : MyBase {
public String Details { get; set; }
}
public class Container {
private static XmlSerializer sSerializer = new XmlSerializer(typeof(Container));
public List<MyBase> mBases { get; set; }
public void MyProblem() {
mBases = new List<MyBase>();
mBases.Add(new MyBase { Title = "One" });
mBases.Add(new MyDerived { Title = "Two", Details = "An incredibly long string" });
using (var lWriter = XmlWriter.Create("C:\\Temp\\output.xml")) {
sSerializer.Serialize(lWriter, this);
}
}
}