Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic solution to simple classes + factory

My tiny mind can't come up with an elegant solution to this problem. Suppose I have class such as this:

    public class Foo<T>
    {
        public RecordType Type { get; set; }
        public T Value { get; set; }
    }

Where RecordType may look something like this:

 public enum RecordType
    {
        EmptyRecord,
        BooleanRecord,
        IntegerRecord,
        StringRecord,
        ByteRecord
    }

The goal is to treat an IEnumerable<Foo<T>> uniformly for an iteration and/or to switch on the RecordType and perform an action while avoiding boxing the intrinsic types if at all possible. In addition, it would be nice to use a factory to create these Foo's off of a factory method.

I've fumbled with a few quick implementations of commonality in base class or interface and nothing I came up with answered this, seemingly, very simple problem elegantly.

Small edit: I should've mentioned that my primary goal is to use the .Value without forcing a cast on the caller.

like image 883
Marc Avatar asked Feb 04 '26 22:02

Marc


1 Answers

You could introduce a non-generic interface IFoo:

public interface IFoo
{
    RecordType Type { get; set; }
}

which is implemented by the generic Foo class:

public class Foo<T> : IFoo
{
    public T Value { get; set; }
}

and create a factory method that creates a Foo instance depending on RecordType:

public static IFoo CreateFoo(RecordType type)
{
    switch (type)
    {
        case RecordType.Bool: return new Foo<bool>();
        // ...
    }
}

Once you've created a Foo instance this way, you can't access the value yet, because you don't know the type parameter yet. But you can check for the type using the Type property and do the appropriate cast:

IFoo foo = CreateFoo(RecordType.Bool);

if (foo.Type == RecordType.Bool)
{
    Foo<bool> boolFoo = (Foo<bool>)foo;
    bool value = boolFoo.Value;
}

If you have a method that works with Foo objects, for example:

void DoIt<T>(IEnumerable<Foo<T>> foos)
{
    foreach (Foo<T> foo in foos)
    {
        Qux(foo.Value);
    }
}

and have an enumerable of IFoo objects, you can Cast/OfType that:

IEnumerable<IFoo> foos = // ...
DoIt<bool>(foos.OfType<Foo<bool>>());

So, essentially, you use Foo<T> when you know T at compile-time, and IFoo if you know T only at run-time. IFoo requires a check to turn it into a Foo<T> for some T at run-time.

like image 126
dtb Avatar answered Feb 07 '26 11:02

dtb