Tornister
Tornister

Reputation: 177

C# Define (multiple) possible types for property of a class

I think I'm experiencing a basic OOP misunderstanding here:

(These are Entity Framework 6 classes btw, in case you're surprised by the "virtual")

public class WeaponUsed : HistoryEvent
{
    public virtual Player Target { get; set; }
    public virtual GameCountry Country { get; set; }
    //Victims: Troops or Pops
    public ICollection<Victim> Victims { get; set; }
    public bool HasMoreWeaponsLeft { get; set; }
}

A victim can be a "Troop" object, or a "Population" object. What does the victim class have to look like? I could use 2 properties and set the unused one to "null" like this:

public class Victim
{
    public virtual Troop TroopVictim { get; set; }
    public virtual Population PopVictim { get; set; }
}

But that can't be the best solution, right? I want to determine that a Victim can be either a Troop or a Population.

I also thought about doing it via the setter:

public ICollection<object> Victims { get; 
    set 
    {
        if (value.GetType() == typeof(Troop) || value.GetType() == typeof(Population))
            Victims.Add(value);
    }
}

But I still don't think that's the best way, or maybe it is... Is there a better, cleaner one?

Upvotes: 2

Views: 5385

Answers (3)

FrankLinTw
FrankLinTw

Reputation: 71

You can use TypeConverter also.

    //
    // Summary:
    //     Converts a string into a font size.
    //
    // Remarks:
    //     To be added.
    [TypeConversion(typeof(double))]
    public class FontSizeConverter : TypeConverter, IExtendedTypeConverter
    {
        public FontSizeConverter();

        //
        // Summary:
        //     Converts a string representation of a font size into a font size.
        //
        // Parameters:
        //   value:
        //     The value to convert.
        //
        // Returns:
        //     To be added.
        //
        // Remarks:
        //     To be added.
        public override object ConvertFromInvariantString(string value);
        public override string ConvertToInvariantString(object value);
    }

and use it like below:

        [TypeConverter(typeof(FontSizeConverter))]
        public double FontSize
        {
            get { return label.FontSize; }
            set { label.FontSize = value; }
        }

Then, you can assign string type or double type...

FontSize="Medium"
/*or*/
FontSize="24"

Upvotes: 0

CodeReaper
CodeReaper

Reputation: 895

Interfaces Mate!

    public interface IVictim
    {
        string SharedProp { get; set; }
    }

    public class TroopVictim : IVictim
    {
        public string SharedProp { get; set; }

        public string TroopProperty { get; set; }
    }

    public class PopVictim : IVictim
    {
        public string SharedProp { get; set; }

        public string PopProperty { get; set; }
    }

    public class MyGenericVictim
    {
        //Optional Property
        public bool? IsTroopVictim
        {
            get
            {
                if (Victim == null) return null;
                return Victim.GetType() == typeof(TroopVictim);
            }
        }

        public IVictim Victim { get; set; }
    }


    public class UseMyOfVictim
    {
        public void Bar()
        {
            Foo(new MyGenericVictim
            {
                Victim = new TroopVictim(),
            });
        }

        public void Foo(MyGenericVictim myvic)
        {
            //Function doesnt know TroopVic or PopVic

            TroopVictim resolvedTroopVic;
            PopVictim resolvedPopVictim;

            if (myvic.Victim.GetType() == typeof(TroopVictim))
            {
                resolvedTroopVic = (TroopVictim) myvic.Victim;
            }
            else if (myvic.Victim.GetType() == typeof(PopVictim))
            {
                resolvedPopVictim = (PopVictim) myvic.Victim;
            }

            string success = resolvedPopVictim.PopProperty;
            success = resolvedTroopVic.TroopProperty;
        }
    }

Upvotes: 0

user3250104
user3250104

Reputation:

You could use interfaces.

public class WeaponUsed : HistoryEvent
{
    public virtual Player Target { get; set; }
    public virtual GameCountry Country { get; set; }
    //Victims: Troops or Pops
    public IVictim Victims { get; set; }
    public bool HasMoreWeaponsLeft { get; set; }
}

public interface IVictim {
    // common methods and properties for PopVictim and TroopVictim
    int Number {get;}
}

public class TroopVictim : IVictim {
    // TroopVictim will be enforced to have IVictim methods and proprieties
    public int Number{ get {return 1; } }
} 

public class PopVictim : IVictim {
    // PopVictim will be enforced to have IVictim methods and proprieties
    public int Number{ get {return 100; } }
} 

Usage:

 Console.WriteLine(weapon.Victims.Number)

Upvotes: 1

Related Questions