Search code examples
c#blazorabstract-class

create a base item that has a dynamic enum as a field


I have different types of item. Each of them has a enum where ID's are keept. I want my base class to have a method to check if the item's ID is on a given list. Something like:

abstract class Thing
{
   public string Name;
   public int Amount;
   //others
   abstract bool IsInList(list<xxxxx> list);
   abstract xxxxxxx ID;
}

class Fruit : Thing
{
   public IDs = {F_NotSet, F_Banana, F_Apple, ...}
   public IDs ID = IDs.F_NotSet;
   public bool IsInList(List<T> list) //this wont compile
   {
      if(typeof(T) == typeof(IDs))
          return list.Contains(IDs);
      else
          return false;
   }
}

The thing is that I also have a (blazor) UI component that visualizes things so I want to be able to do my

<div>@Thing.Name [@Thing.Amount]</div>
<code>
   [Parameter] public Thing Thing {get;set;}
</code>

And use it on my page for all kind of things, like :

<div>
@foreach(var thing in Things) //things being List<Thing>
{
   <ThingViewer ItemToShow=thing/>
}
</div>

That's why I don't want to go the Thin<T>path because then my UI component to visualize Things and my page gets messy.

On the other side, I would like to use this "IsInList" method from the page to do things like

<div>
@foreach(var thing in MyThings) //MyThings being List<Thing>
{
   @if(thing.IsInList(ThingsInOffer))
   {
      <div class="offer">
        <ThingsVisualizer ItemToShow=thing/>
      </div>
   }
}
</div>

Solution

  • Here is the complete working example from fiddle, that helped solve the issue:

    using System;
    using System.Linq;
    using System.Text.RegularExpressions;
    using System.Collections.Generic;
                        
    public class Program
    {
        // Just for showcasing:
        public static void Main()
        {
            var apple = new Fruit(Fruits.Apple);
            var banana = new Fruit(Fruits.Banana);
            var orange = new Fruit(Fruits.Orange);
            var strawberry = new Nut(Nuts.Strawberry);
            
            var assortedFruits = new []{ Fruits.Apple, Fruits.Orange };
            
            Console.WriteLine("I ({0}) am {1}an assorted fruit!", apple.Id, apple.IsInList(assortedFruits)?"":"not ");
            Console.WriteLine("I ({0}) am {1}an assorted fruit!", banana.Id, banana.IsInList(assortedFruits)?"":"not ");
            Console.WriteLine("I ({0}) am {1}an assorted fruit!", strawberry.Id, strawberry.IsInList(assortedFruits)?"":"not ");
            
            PrintDetails(apple);
            PrintDetails(banana);
            PrintDetails(orange);
            PrintDetails(strawberry);
            
            foreach( var thing in new IThing[]{apple, strawberry} )
            {
                Console.WriteLine($"{thing.Name}s have {thing.SomeProperty}");
            }
        }
        
        public static void PrintDetails(IThing thing)
        {
            // Going by an interface: We do not really care, what kind of 
            // "thing" it is in particular.
            thing.Print();
        }
    }
    
    // Two "Kinds" of Things: Fruits and Nuts
    
    public enum Fruits
    {
        Apple,
        Banana,
        Orange
    }
    
    public enum Nuts
    {
        Strawberry
    }
    
    // Things all kinds of "Things" need to be able to do:
    
    public interface IThing
    {
        void Print();
        string Name {get;}
        string SomeProperty {get;}
    }
    
    // Common generic implementations:
    public abstract class Thing<TIdentifyingThing> : IThing
    {
        protected TIdentifyingThing _me;
        
        protected Thing(TIdentifyingThing id)
        {
            _me = id;
        }
        
        public TIdentifyingThing Id => _me;
        public string Name => _me.ToString();
        public abstract string SomeProperty {get;}
    
        // I think the "thing" here was that you can have generic methods
        // with Types that do not need to be the same as the generic class's
        // type. Here `T` versus `TIdentifyingThing`.
        public bool IsInList<T> (IEnumerable<T> list)
        {
            if( typeof(T) != typeof(TIdentifyingThing) ) return false;
            if( list is not null && list.Any() )
            {
                return list.Cast<TIdentifyingThing>().Contains(_me);
            }
            return false;
        }
        
        public abstract void Print();
    }
    
    // The specific Things:
    
    public class Fruit : Thing<Fruits>
    {
        public Fruit( Fruits identity ): base(identity) {}
        
        public override string SomeProperty => "just some property.";
        
        public override void Print()
        {
            Console.WriteLine($"My fruity details are: I am a{(Id.ToString().StartsWithVocal() ? "n" : "" )} {Id}.");
        }
    }
    
    public class Nut : Thing<Nuts>
    {
        public Nut( Nuts identity ): base(identity) {}
        
        public override string SomeProperty => "not always the appearance you expect.";
        
        public override void Print()
        {
            Console.WriteLine($"My nutty details are: I am a{(Id.ToString().StartsWithVocal() ? "n" : "" )} {Id}.");
        }
    }
    
    
    // Just so the code is complete. Doesn't actually contribute to the solution as such.
    public static class StringExtensions
    {
        public static bool StartsWithVocal(this string me)
        {
            return Regex.IsMatch(me, "^[aeiouAEIOU]");
        }
    }