0

I have two classes - Expert and Farmer, that both share some common characteristics and thus implement an abstract class User. User implements an interface IUser. In my application initially common fields have been filled out but then the user indicates whether they are an Expert/Farmer.

At this point in time, I want to cast my current object into either an Expert or a Farmer to fill in the individually unique fields. My current approach is to declare IUser user = new Farmer(); and then try and cast appropriately at the appropriate time: Farmer farmer = user as Farmer OR Expert expert = user as Expert. However, the cast to an Expert always fails, although they both implement the same interface and abstract class.

  1. Why is this cast failing?
  2. What is the correct way to solve this issue neatly using inheritance? Obviously one could create functions createUser() or createFarmer() defined in the User abstract class or just instantiate a User and a Farmer and fill the initial fields in both, taking whichever instance is required at the split time, but neither of these are particularly elegant.

Definition of Expert:

public partial class Expert : User
    {

        public override Guid Id { get; set; }
        [StringLength(200)]
        public override string Name { get; set; }
        [Key]
        [StringLength(10)]
        public override string phone { get; set; }
        [StringLength(50)]
        public override string state { get; set; }
        [StringLength(50)]
        public override string district { get; set; }
        [StringLength(6)]
        public override string PIN { get; set; }
        public override double geolat { get; set; }
        public override double geolong { get; set; }
        public override int v { get; set; }
        public override int h { get; set; }
        public override string AzureImageURL { get; set; }

        //IParty properties
        public override string ServiceUrl { get; set; }
        public override string ChannelId { get; set; }
        public override string PartyChannelAccountId { get; set; }
        public override string PartyChannelAccountName { get; set; }
        public override string BotChannelAccountId { get; set; }
        public override string BotChannelAccountName { get; set; }
        public override string ConversationAccountId { get; set; }
        public override string ConversationAccountName { get; set; }


        //Expert specific properties
        public double price { get; set; }
        public int totalRating { get; set; }
        public int numberOfRating { get; set; }
        public string description { get; set; }
        public string languages { get; set; }
        double _sortIndex = double.Nan
    }

Definition of Farmer:

public partial class Farmer : User, IVisualizable
    {
        //User override fields
        [Key]
        public override Guid Id { get; set; }
        [StringLength(200)]
        public override string Name { get; set; }
        [StringLength(10)]
        public override string phone { get; set; }
        [StringLength(50)]
        public override string state { get; set; }
        [StringLength(50)]
        public override string district { get; set; }
        [StringLength(6)]
        public override string PIN { get; set; }
        public override double geolat { get; set; }
        public override double geolong { get; set; }
        public override int v { get; set; }
        public override int h { get; set; }
        public override string AzureImageURL { get; set; }

        //IParty properties
        public override string ServiceUrl { get; set; }
        public override string ChannelId { get; set; }
        public override string PartyChannelAccountId { get; set; }
        public override string PartyChannelAccountName { get; set; }
        public override string BotChannelAccountId { get; set; }
        public override string BotChannelAccountName { get; set; }
        public override string ConversationAccountId { get; set; }
        public override string ConversationAccountName { get; set; }

        //Farmer specific properties
        [StringLength(12)]
        public string AadhaarNum { get; set; }
        public int cropId { get; set; }
        [StringLength(1)]
        public string acreage { get; set; }

        /// <summary>
        /// Constructor calling base - sets Guid of farmer
        /// </summary>
        public Farmer() : base()
        {

        }

    }

Definition of User:

public abstract class User : IUser
    {
        public abstract Guid Id { get; set; }
        public abstract string Name { get; set; }
        public abstract string phone { get; set; }
        public abstract string state { get; set; }
        public abstract string district { get; set; }
        public abstract string PIN { get; set; }
        public abstract double geolat { get; set; }
        public abstract double geolong { get; set; }
        public abstract int v { get; set; }
        public abstract int h { get; set; }
        public abstract string AzureImageURL { get; set; }
        public abstract string ServiceUrl { get; set; }
        public abstract string ChannelId { get; set; }
        public abstract string PartyChannelAccountId { get; set; }
        public abstract string PartyChannelAccountName { get; set; }
        public abstract string BotChannelAccountId { get; set; }
        public abstract string BotChannelAccountName { get; set; }
        public abstract string ConversationAccountId { get; set; }
        public abstract string ConversationAccountName { get; set; }

        /// <summary>
        /// Creates a new User with a new Guid already set.
        /// </summary>
        public User()
        {
            Id = Guid.NewGuid();
        }

    }

Definition of IUser:

public interface IUser : IParty
    {
        Guid Id { get; set; }
        string Name { get; set; }
        string state { get; set; }
        string district { get; set; }
        string PIN { get; set; }
        double geolat { get; set; }
        double geolong { get; set; }
        int v { get; set; }
        int h { get; set; }
        string AzureImageURL { get; set; }

    }

Thanks!

Varun Mathur
  • 898
  • 8
  • 11

1 Answers1

2

How would you expect a farmer to become an expert? Even if they implement the same common interface, they are completely different. In particular your classes have some uncommon members:

class Expert
{
    public string languages { get; set; }
}
class Farmer
{
    public string AadhaarNum { get; set; }
}

How should the languages be transformed into a AadhaarNum?. That´s impossible and thus there´s no automatic cast. You´d have to create some own cast or transformation, e.g.

var farmer = new Farmer(expert);

This assumes you have a copy-constructor within both classes:

class Farmer
{
    public Farmer(IUser user)
    {
        this.Name = user.Name;
        ...
        // also set the members that exist only on farmer
    }
}

and for the Expert-class accordingly.

Alternativly you can introduce some factory-methods within your abstract base-class. However this has the disadvantage that your base-class needs to know all of it´s possible sub-types. Introducing new sub-classes thus leads to changing the base-class.

MakePeaceGreatAgain
  • 35,491
  • 6
  • 60
  • 111
  • More specifically, in C# the cast is only allowed *if the name-hierarchy structure says it's allowed* (nominative typing). The same names / member signatures in different types are not compatible unless subscribing to the same "interface"; compare to structural typing. – user2864740 Aug 30 '17 at 06:12
  • @user2864740 I´m not sure how this applies to the answer. There are no classes sharing the same name. – MakePeaceGreatAgain Aug 30 '17 at 06:15
  • The example/wording allows for the interpretation that it's "impossible to be transformed because of uncommon members". It makes it impossible to *inherit from* the same unified type spanning the union of such members.. as the types only implement a strict subset. – user2864740 Aug 30 '17 at 06:17
  • Just edited the question to add class definitions - what I want to do is have some object with which I can fill in the common members (defined in User) and then be able to cast this object into a Farmer/Expert. At the point of casting I'm 100% sure that the only fields that will be filled in are the ones that are common between both classes. – Varun Mathur Aug 30 '17 at 06:20
  • Use a copy-constructor as shown above. No need to cast, just create a new instance of your derived type based on an existing instance of your base-class/interface. Anyway I added your specific needs into my answer. – MakePeaceGreatAgain Aug 30 '17 at 06:21
  • Ok - that's sort of what I was getting at when I said "one could create functions createUser() or createFarmer() defined in the User abstract class" but just wasn't sure if there was a more elegant way to do it. – Varun Mathur Aug 30 '17 at 06:24