Return a concrete type in an abstract class

We have an abstract BaseClass (note the general argument!) Using a method called me. I am returning this.

If we use Me in specific classes, we get an object of type return. Then we must cast the result of Me to the type with which we initially work.

How can we achieve that Me returns the actual type of this? In this example, type A?

public abstract class BaseClass<TIdentifier>{ public virtual object Me{ get { return this; } } } public class A: BaseClass<long> { } public class B: BaseClass<long> { } public class controller{ public void SomeMethod(){ var a = new A(); var b = new B(); var aObject = a.Me; // this will be of type object var aObjectCasted = (A)aObject; // cast to original // How I want it var aConcrete = a.Me; // this returns type a } } 

Update

Since some people really, desperately (wink :-)), want to understand what I'm actually trying to do.

At NHibernate we do this:

 var result = Session.Get<A>(idToLookUp); 

In some cases, it happens that the result is not of type A, but of type AProxy, due to the impossibility of loading, etc. Now, if we want to distinguish the result from something else: we will get an invalid exception, because the actual type of the result is not A, but AProxy. And this type cannot be discarded. We can only use type A for another type.

A workaround for this is described here: http://sessionfactory.blogspot.be/2010/08/hacking-lazy-loaded-inheritance.html . Where the Me property is in the above examples.

So, to get the result of type A, and not of type AProxy, we must do this:

 var result = (A)Session.Get<A>(idToLookUp).Me; 

Please note that we must return me to type A if we want to read and find out the property of the result.

My question is: can we get rid of casting and set the Me property so that we immediately return a specific type?

Hope this is clear now.

+5
source share
5 answers

You can use the interface for your derived classes:

 public interface IStrongTypedMe<T> { T Me(); } 

Your derived classes will become:

 public class A: BaseClass<long>, IStrongTypedMe<A> { public new A Me() { return base.Me() as A; } } 

This suggests that you can change A , of course.

Update:

Now I understand the problem (I only had time to read the related article).

Try using the extension method to cast for you as follows:

  public static TReturnType As<TReturnType,TIdentifier>(this BaseClass<TIdentifier> proxyObject) where TReturnType : class { return proxyObject.Me as TReturnType; } 

And you would use it like:

 var result = Session.Get<A>(idToLookUp).As<A,long>(); 

No changes to A or B are required.

+3
source

You can change the return type of this property to the definition of the parent class

 public abstract class BaseClass<TIdentifier> { public virtual BaseClass<TIdentifier> Me{ get { return this; } } } 

If you want to return the exact same class, you can make some workaround by adding the result type to the type parameter

 public abstract class BaseClass<TIdentifier, TMe> where TMe : BaseClass<TIdentifier, TMe>, new() { public virtual TMe Me { get { return (TMe)this; } } } public class A : BaseClass<long, A> { } 
+5
source

Unfortunately, C #, unlike Java , does not support return type covariance. Otherwise, you can simply override the Me property in subclasses like this to get what you want:

 public abstract class BaseClass<TIdentifier> { public virtual object Me { get { return this; } } } public class A: BaseClass<long> { public override A Me { get { return this; } } // wont work in C# } public class B: BaseClass<long> { public override B Me { get { return this; } } // wont work in C# } 

Mikhail Neofitov provides a good workaround.

+2
source

To do something like this:

 var aObject = A.Me(); 

Me should be a static method.

  • The static method does not have this .
  • If yours is not using a static method, you have this - otherwise how do you want to call a class method? You just need to give it to the right type.

Update due to change:

You have this code:

 var a = new A(); var aObject = a.Me; 

Now what do you expect here?
You have a that has type a .
Using var , you cannot have several different return types from Me geter.

0
source

The problem is implicitly defining a variable with var . When you use var , in this case, the compiler cannot determine the correct type for aObject in the editor. So take the following code, for example:

 public abstract class BaseClass<TIdentifier> { public virtual object Me {get {return this;} } } public class A : BaseClass<TIdentifier> { public int X { get {return 1;} } } public class B : BaseClass<TIdentifier> { } public class controller{ public void SomeMethod(){ var a = new A(); var b = new B(); var aObject = a.Me; var aObjectCasted = (A)aObject; // the environment cannot determine the correct type for aObject // without compiling and running. At this time in the editor, // this will be recognized as a type object. It will not // understand aObject.X and will not compile Console.WriteLine(aObject.X); // During run-time, this will work. aObject will be defined as type A Console.WriteLine(aObject.GetType().GetProperty("X").GetValue(aObject)); // this will output A for the type Console.WriteLine(aObject.GetType()); } } 

Without the ability to modify A and B using GetProperty, GetMethod, etc. for an implicitly defined variable, it looks like this will be your only hope.

Update: You can reference this to view the types of calls you can make on the Type object. It looks like you will need to do this more dynamically in order to achieve the desired functionality. An object will not be defined correctly before compilation if you try to do it implicitly.

var aConcrete = a.Me; in your code will really return a type A value for aConcrete at compile time, but not in the editor.

From MSDN : β€œIt is important to understand that the var keyword does not meanβ€œ option ”and does not indicate that the variable simply means that the compiler defines and assigns the most suitable type .

0
source

Source: https://habr.com/ru/post/1241708/


All Articles