I am using NHibernate for my C# pojects and therefore I have several model classes.
Lets assume the following example:
using System;
namespace TestProject.Model
{
public class Room
{
public virtual int Id { get; set; }
public virtual string UniqueID { get; set; }
public virtual int RoomID { get; set; }
public virtual float Area { get; set; }
}
}
Mapping these objects with NHibernate works fine so far. Now I want to generate a new Room object and I want to store it in the database. To avoid setting each member seperatly, I add a new constructor to the model class.
Below the virtual members I write:
public RoomProperty()
{
}
public RoomProperty(int pRoomId, int pArea)
{
UniqueID = Guid.NewGuid().ToString();
RoomID = pRoomId;
Area = pArea;
}
Analyzing my code with FxCop tells me the following:
"ConstructorShouldNotCallVirtualMethodsRule"
This rule warns the developer if any virtual methods are called in the constructor of a non-sealed type. The problem is that if a derived class overrides the method then that method will be called before the derived constructor has had a chance to run. This makes the code quite fragile.
This page also describes why this is wrong and I also understand it. But I am not shure how to solve the problem.
When I erase all constructors and add the following method...
public void SetRoomPropertyData(int pRoomId, int pArea)
{
UniqueID = Guid.NewGuid().ToString();
RoomID = pRoomId;
Area = pArea;
}
.... to set the data after I called the standard constructor I cant start my aplication becaue NHibernate fails initializing. It says:
NHibernate.InvalidProxyTypeException: The following types may not be used as proxies:
VITRIcadHelper.Model.RoomProperty: method SetRoomPropertyData should be 'public/protected virtual' or 'protected internal virtual'
But setting this method to virtual would be the same mistake as when I just set the virtual members in the constructor.
How can I avoid these mistakes (violations)?
The problem lies in virtual set. Passing a value to the virtual property in the base class constructor will use overriden set instead base set. If overriden set relies on data in derived class, then you are in trouble, because constructor of derived class was not done yet.
If you are absolutely sure, that any subclass will not use any data of its state in overriden set, then you can initialize virtual properties in base class constructor. Consider adding an appropriate warning to the documentation.
If possible, try to create backing fields for each property and use them in base class contructor.
You can also postpone properties initialization to the derived class. To achieve that, create an initializing method in the base class that you invoke in constructor of derived class.
I expect one of the following to work:
Make the properties non-virtual (preferred as long as NHibernate supports it).
Change from auto-implemented properties to properties with an explicit backing field, and set the fields in the constructor instead of setting the properties.
Create a static Create method which constructs the object first, and then sets values to the properties before returning the constructed object.
Edit: From the comment I see option #3 was not clear.
public class Room
{
public virtual int Id { get; set; }
public virtual string UniqueID { get; set; }
public virtual int RoomID { get; set; }
public virtual float Area { get; set; }
public static Room Create(int roomId, int area)
{
Room room = new Room();
room.UniqueID = Guid.NewGuid().ToString();
room.RoomID = roomId;
room.Area = area;
return room;
}
}
IMHO, good idea is to make base class - abstract and its constructor - protected.
Next, inherited classes have their constructor private and - for outer world - uniform static method like "Instance", which first of all, initializes the constructor, then - calls whole set of class methods in correct sequence and finally - returns the instance of the class.
Related
As you know, C# 9.0 (.Net 5) now allows Covariant Returns. I need help applying this to a set of classes having Auto-Implemented properties.
I have two abstract classes that represent financial bank accounts and transactions. I made them abstract since I will pull data from various data sources and while the main properties will be common across all sources, each source may have additional fields I want to keep. A 1 to Many relationship exists between both classes (1 account has many transactions AND 1 transaction belongs to only 1 account).
public abstract class BankAccount
{
public string Name { get; set; }
public IList<Transaction> Transactions { get; set; } = new List<Transaction>();
...
}
public abstract class Transaction
{
public string Name { get; set; }
public virtual BankAccount BankAccount { get; set; } // This doesn't work unless I remove set;
...
}
And here is an example of the concrete implementations
public class PlaidBankAccount : BankAccount
{
public string PlaidId { get; set; }
...
}
public class PlaidTransaction : Transaction
{
public string PlaidId { get; set; }
public override PlaidBankAccount BankAccount { get; set; } // This doesn't work unless I remove set;
...
}
What I want to do is to override the base class getters and setters so that they use derived classes. For example:
If I create an instance of the concrete transaction and call the BankAccount getter, I want to get an instance of the derived PlaidBankAccount not the base BankAccount.
What I've found is that when I only define virtual getter in the base class and override it in the derived class, it works. But just as I add both properties {get;set;}, I get the same error as in previous C# versions:
error CS1715: 'PlaidTransaction.BankAccount': type must be 'BankAccount' to match overridden member 'Transaction.BankAccount'
How could I fix this?
In C# 9 properties are only able to have co-variant returns when they are readonly, so unfortunately, no set; is possible.
An overriding property declaration must specify exactly the same access modifier, type, and name as the inherited property. Beginning with C# 9.0, read-only overriding properties support covariant return types. The overridden property must be virtual, abstract, or override.
From the Microsoft Docs - Override keyword
I have an app that uses a set of dll's from a 3rdparty. I am trying to incorporate an updated version of the dll's that have changed some variables and parameters from int to uints. I think I can easily capture base class events in my derived class and re-throw modified events, but I am not sure of an easy way to handle the direct access of the variables in the base class's member class.
The example below shows the original 3rd party implementation. In the latest version, the member variables of ThirdPartyNumberPair are now uint's. I'm looking for a way to intercept the MyNumberPair.x and .y access in my derived container and do the conversion so I don't have to modify SomeMethod - mainly because it is used in many places.
public class ThirdPartyNumberPair
{
public int x{ get; set; };
public int y{ get; set; };
}
public class ThirdPartyContainer
{
public ThirdPartyNumberPair MyNumberPair;
}
public class MyDerivedContainer : ThirdPartyContainer
{
...
}
public class MyClass
{
public MyDerivedContainer myContainer;
public void MyMethod(){
int i = myContainer.MyNumberPair.x;
myContainer.MyNumberPair.y = 3;
}
}
I've tried creating a derived MyThirdPartyNumberPair and hiding the base ThirdPartyNumberPair, but I didn't find any easy way of getting those values to the base ThirdPartyNumberPair member.
I have an interface that declares some properties (shortened to Id only in the example) with only a get method. Classes implementing this interface do not have to provide a public setter for this property:
public interface IMetadataColumns
{
Int32 Id { get; }
}
Now I need another interface with the same properties, but now they must all be writable as well. So I added a new interface inheriting from the old one where each property also has a setter:
public interface IMetadataColumnsWritable : IMetadataColumns
{
Int32 Id { get; set; }
}
Visual Studio now underlines this and warns me to use the new keyword if hiding the old properties was intended.
What shall I do now? I have classes implementing the IMetadataColumns interface which need some of the properties to be read-only, but I also have other classes where exactly those same properties must be writable as well.
I guess hiding a property sounds somehow not like the way to go...
When interfaces are involved, the new keyword doesn't mean you are hiding the property in the same sense as with classes.
Your class implementing IMetadataColumnsWritable will only have one Id, regardless of whether you cast it to the base IMetadataColumns interface or not (unless you add an explicit implementation for the readonly property - but this is not needed and would only allow for errors in this case).
In other words, you might have:
// this interface is public, and it allows everyone to read the Id
public interface IMetadataColumns
{
int Id { get; }
}
// this one is writeable, but it's internal
internal interface IMetadataColumnsWritable : IMetadataColumns
{
// we need to use 'new' here, but this doesn't mean
// you will end up with two getters
new int Id { get; set; }
}
// internal implementation is writeable, but you can pass it
// to other assemblies through the readonly IMetadataColumns
// interface
internal class MetadataColumns : IMetadataColumnsWritable
{
public int Id { get; set; }
}
So, unless you explicitly implement the readonly property as a separate one, you will have only a single property, whichever interface you use to access it:
var columns = new MetadataColumns { Id = 5 };
var x = (columns as IMetadataColumns).Id;
var y = (columns as IMetadataColumnsWritable).Id;
Debug.Assert(x == y);
Hiding a member inside the class using the new keyword, on the other hand, is what I would not recommend in most cases, because it basically creates a completely new member, which just happens to be called the same as a different member in the base class.
You should indeed use the new keyword here. In your class implementation, you have to implement both properties (since you are not really hiding the property). If they have different return types, at least one of them has to be implemented explicitly.
public class X : IMetadataColumnsWritable
{
public int Id
{
get;
set;
}
// only necessary if you have to differentiate on the implementation.
int IMetadataColumns.Id
{
get
{
return this.Id; // this will call the public `Id` property
}
}
}
Does using explicit implementation of the getter only version forwarding to a public version work?
int IMetadataColumns.Id {
get {
return this.Id;
}
}
public Id {
get { … }
set { … }
}
You might find the public version does need to be new, but the explicit version will be picked up by references of type IMetadataColumns.
is there any diffrence between this 3 auto properties ?
interface MyInterface {
public int p1 { get; set; }
public int p2 { get; }
public int p3 { set; }
}
also why we can write this code in an interface but not in a class ?
public int p { get; }
For the same reason you can write this in an interface:
interface IFace {
void Test();
}
Also, your interface is invalid, as public isn't valid in an interface. The point being, different things are legal in interfaces and classes.
When you do public int P1 { get; set; } in a class, that turns into a auto property. However, you can't do public int P1 { get; }, because what would you want that to mean? Should it always return 0? There is no way to set it. So if you want a read only property you have to define the getter yourself like this:
int _p1;
public int P1 {
get { return _p1; }
}
Also. Another way to achieve more or less the same is this:
public int P1 { get; private set; }
There are differences between those properties. Firstly, you should remove the public modifier from your declaration. Secondly, by putting get or set within the block you define what properties in derived classes should look like. For example, public int p1 { get; set; } requires getter and setter in a derived class, public int p2 { get; } only getter, and public int p3 { set; } requires only setter to be implemented.
You can't use access modifiers inside interfaces because interfaces are guidelines for other developers that force them to go in a certain direction when developing the implementing classes.
Look at this post for more information about that.
Keep in mind interface does NOT contain any implementation data. When you add property in an interface, it merely says that a class implementing this interface needs to have said property with get, set or both methods, depending on what you wrote. So any class implementing your interface has to implement (or have auto-generated) p1 property with get and set method, p2 with get method, and p3 with set method. Interface doesn't care whether these will be auto-generated or your own custom implementations, they just have to be in an implementing class.
Therefore, you can write
int p { get; }
in an interface as all it does is telling that any class implementing this interface has to have property p with getter, again, not caring about its actual implementation - you could write a getter that does some computations, returns some constant, etc. OTOH in a class writing the same would mean that you want a property with auto-generated backing field, except since it would have no setter, you couldn't actually change its value, so it would always have its default value 0.
And as noted, you cannot write access modifiers in an interface, as all interface members are implicitly public.
I have a class like the below, I want to override the set value of "School,Country..etc.." property when some one sets a value , i don't want to change the student class but i need to do it in the base class and use it as a generic method
public class Student : BaseClass
{
public String School { get; set; }
public String Country{ get; set; }
}
ie:
When some one sets
Student.School="Harvard",
I need to store it as
Student.School="Harvard my custom value";
Note:
Basically calling OnPropertyChanged in base class rather than the main class.
If you want to do it with aspects, then try Postsharp
Basically you cannot override a non-virtual property. You can hide it by other property with the same name in the derived class, but this won't give you the desired effect if some other code accesses your object by the reference to the base class.
public class Student : BaseClass
{
private string _school
public string School
{
get { return _school; }
set
{
if(value == "Harvard")
value = "Harvard custom";
_school = value;
}
}
public String Country{ get; set; }
}
is that what you mean?
If the School property is in the BaseClass then you can either use the new keyword, or if you control the BaseClass, then you can add the virtual keyword to the School property there, and override it in the Student class.
This is just not doable by solely modifying BaseClass. Think about it this way: If it were possible to "annotate" automatic properties that easily, then we wouldn't need all those <rant>useless tons of</rant> manual property implementations for data model classes that implement INotifyPropertyChanged (same for DependencyProperties).
You need to provide hooks in your subclasses that your base class can use. Implementing PropertyChanged, which you already mentioned, is one possible solution, another one would be a simple method call:
public class Student : BaseClass
{
private string _school;
public String School
{
get { return _school; }
set {
_school = value;
DoMoreChanges(ref _school); // DoMoreChanges is defined in BaseClass
}
}
public String Country{ get; set; }
}
If you have lots of subclasses that need this, you can either use Visual Studio Code Snippets to create the code or T4 templates.
Since your base class does not have those properties you will not be able to modify them from within the base class using standard OOD patterns or principles.
Now if you move the properties to your base class either as normal properties or virtual properties you can modify what you do in the set block of the properties to do extra work.
However if you cannot move these to the base class, and you cannot modify the Student class, as you seem to imply in you question, then you could encapsulate the student class within a new class like StudentProxy or something and then have it expose similar properties that will then call into the real student class how you want.
For example:
public class StudentProxy
{
private Student _student;
public StudentProxy(Student student)
{
this._student = student;
}
public String School
{
get { return _student.School; }
set
{
_student.School = value + " my custom value";
}
}
public String Country
{
get { return _student.Country; }
set
{
_student.Country = value + " my custom value";
}
}
}