• 0

abstract class <T> where T is resolved at runtime.


Question

I'm having trouble finding the right solution for what I am trying to achieve. The closest thing I can come up with is to set the child classes with object and casting (which is ugly to me).

I basically want to define basic domain objects that work with a specific type of data, that differs per class. The code below will not compile and I know this, I'm trying to show you want

I want to achieve. It's pretty basic until the Main function.

public abstract class WorkerTypeBase<T>
{
      // other methods and properties here
      abstract T GetResult();
      abstract void SetResult(T value);
}
 
public ChildWorkerClass1 : WorkerTypeBase<ResultType1>
{
      private ResultType1 _resType1;
 
      public override ResultType1 GetResult()
      { return _resType1; }
      public override void SetResult(ResultType1 value) { _resType = value; }
}
 
public ChildWorkerClass2 : WorkerTypeBase<string>
{
      private string _resType1;
 
      public override string GetResult()
      { return _resType1; }
      public override void SetResult(string value) { _resType = value; }
}
 
static int Main()
{
      Dictionary<string, WorkerTypeBase<>> myDictionary = new Dictionary<string, WorkerTypeBase<>>();
      WorkerTypeBase def = _factory.GetRandomWorkerBaseType("<Param makes factory return ChildWorkerClass2>"); // This will not work, you have to specify the <T> at compile time. I don't want to have to care what T is at this point in time.
      myDictionary.Add("1001", def);
      Console.Writeline("Result: {0}", (def as WorkerTypeBase<string>).GetResult());
      WorkerTypeBase def2 = _factory.GetRandomWorkerBaseType("<Param makes factory return ChildWorkerClass1>");
      myDictionary.Add("1002", def2);
      Console.Writeline("Result: {0}", (def2 as WorkerTypeBase<ResultType1>).GetResult().CustomString);
}

I don't want to have to care about what T is unless I specifically want to work with the data that object would be holding. The only other thing I can think of is attaching an interface and then casting per child class, but that is an even uglier version of the object casting solution. I'm not sure if a dynamic keyword option is available or not. Maybe this isn't even possible and I'm making it too complicated, but it seems like there is something I'm missing here.

Link to comment
Share on other sites

4 answers to this question

Recommended Posts

  • 0

Type erasure is not supported C#

EDIT,

You can use Dictionary<string, dynamic> myDictionary

and

dynamic def

in your example

EDIT 2,

I would do a common interface (IWorkerTypeBase) then

you can either add functions that give back Object to IWorkerTypeBase (IE: Object GetResult())

or use dynamic

Edited by _Alexander
Link to comment
Share on other sites

  • 0

What about something like this? I just moved the common part to an interface, and then did an explicit implementation of the interface in the abstract class. So if you have a reference to an IWorkerType result is typed as an object. If you have a reference to WorkerTypeBase<T> then result is typed as T.

public interface IWorkerType
{
    object GetResult();
    void SetResult(object result);
}

public abstract class WorkerTypeBase<T>
    : IWorkerType
{
    object IWorkerType.GetResult()
    {
        return GetResult();
    }    

    void IWorkerType.SetResult(object result)
    {
        if (!(result is T))
        {
            throw new ArgumentException(string.Format("Must be of type {0}", typeof(T).FullName), "result");
        }

        SetResult((T)result);
    }

    public T GetResult();
    public void SetResult(T result);
}

?

?

?

?

With this set up all you have to do is make your Dictionary a Dictionary<string, IWorkerType> and the rest of your code works as you have it.

Link to comment
Share on other sites

  • 0

What about something like this? I just moved the common part to an interface, and then did an explicit implementation of the interface in the abstract class. So if you have a reference to an IWorkerType result is typed as an object. If you have a reference to WorkerTypeBase<T> then result is typed as T.

public interface IWorkerType
{
    object GetResult();
    void SetResult(object result);
}

public abstract class WorkerTypeBase<T>
    : IWorkerType
{
    object IWorkerType.GetResult()
    {
        return GetResult();
    }    

    void IWorkerType.SetResult(object result)
    {
        if (!(result is T))
        {
            throw new ArgumentException(string.Format("Must be of type {0}", typeof(T).FullName), "result");
        }

        SetResult((T)result);
    }

    public T GetResult();
    public void SetResult(T result);
}

?

?

?

?

With this set up all you have to do is make your Dictionary a Dictionary<string, IWorkerType> and the rest of your code works as you have it.

I hadn't actually thought about the explicit declarations from an interface, I kind of like that idea. Thanks to all those who have replied. I knew I wouldn't be able to get the exact solution I wanted, but I was trying to get some brainstorming going on how to efficiently get it as close as possible.

 

EDIT: Can a mod change the word in my title to be the correct spelling of "resolved"? I don't know how I overlooked that, but that's bugging me every time I look at this thread lol.

Link to comment
Share on other sites

  • 0

You didn't specify what common operations all WorkerType<T>s share, so their common subtype could as well be object or an empty interface, but now what's the point of storing a bunch of those objects together if there's nothing you can do with them? If your design implies casting to the right type to use them then you should fix your design so that the work is done by the WorkerType<T> itself (who knows its own type), rather than another object that you're trying to have manage multiple objects of unrelated types.

 

By the way, perhaps this was a contrived example but Get/Set methods are not idiomatic C#, you would use properties for this purpose.

 

Also you improperly use the "as" operator, this:

(t as T).DoSomething()

says "it's perfectly normal that the cast fails but I fully expect it to succeed so I can call a method on the resulting value", which is self-contradictory. If you expect the cast to succeed, just use a regular cast:

((T)t).DoSomething()

and now at least if it fails you get an InvalidCastException which is much more informative than a NullReferenceException.

  • Like 3
Link to comment
Share on other sites

This topic is now closed to further replies.