0

I have classes/interfaces similar to the following:

    public interface IValueToken
    {
    }

    public abstract class ProgramToken
    {
        public ProgramToken(string name)
        {
            Name = name;
        }

        public string Name { get; }
    }


    public class ValueToken<T> : ProgramToken, IValueToken
    {
        public ValueToken(string name, T value) : base(name)
        {
            Item = value;
        }

        T Item { get; }
    }


There are different generic ValueToken<T> types and they are held in a List<ProgramToken>, along with other types derived from ProgramToken

I need to do similar to this:

            var token = new ValueToken<int>("int", 78);

            if (token is IValueToken ivt)
            {
                var value = ivt.Value;
                // do something with value
                ....
            }

Some posts here at SO mentioned doing similar to how List does it, implementing the non-generic interface explicitly. I thought it was something like this:

Change IValueToken to

    public interface IValueToken
    {
        object Item { get; }
    }

and ValueToken to

    public class ValueToken<T> : ProgramToken, IValueToken
    {
        public ValueToken(string name, T value) : base(name)
        {
            Item = value;
        }

        object IValueToken.Item { get { return (T)Item; } }

        T Item { get; }
    }

But this isn't working the same as List<T>. It returns an object type. What am I doing wrong? Or what is a better way of accomplishing this?

DaveDod
  • 9
  • 1
  • 1
  • "It returns an object type." What type did you expect it to return? Also, your list is a list of `ProgramToken`s, which does not necessarily contain `IValueToken`s. Did you mean to have a list of `IValueToken`s instead? – Sweeper Apr 03 '23 at 01:28
  • What is `// do something with value` actually going to be? – Charlieface Apr 03 '23 at 01:36
  • Side note: you may want to carefully read https://stackoverflow.com/questions/961581/whats-the-difference-between-dynamic-c-4-and-var – Alexei Levenkov Apr 03 '23 at 02:14
  • @Sweeper No the list contains other types derived from 'ProgramToken'. – DaveDod Apr 03 '23 at 02:19
  • @Charlieface In the actual implementation it's only important to get the value. At the places the value is required it just needs to be retrieved. eg, `T` could be int or could be double. Both can be added to a `List` without explicitly casting – DaveDod Apr 03 '23 at 03:07
  • 2
    If you declare a property as type `object` then how could it possibly return anything other than an `object` reference? Casting `Item` as `T` in the `IValueToken.Item` getter is pointless. `Item` is already type `T` so the cast does nothing and `IValueToken.Item` is type `object` so any object will be returned as a reference of type `object`, no matter what. – jmcilhinney Apr 03 '23 at 05:46
  • @jmcilhinney Yes I realize that now. I've tried so many ways to do this and am totally confused. Is this not possible then? Do I really have to stick to type-checking every type T could be and casting to that type to access the Value property? – DaveDod Apr 03 '23 at 06:34
  • Well one option would be adding a `public double CastedValue { get; }` to the interface, then each generic type will have to find a way of implementing it, either by casting or parsing. – Charlieface Apr 03 '23 at 09:30

1 Answers1

0

According to what you've said in comments, you just need it to return a double.

In .NET 7 you can do this by constraining to INumberBase<T>

public class ValueToken<T> : ProgramToken, IValueToken
    where T : INumberBase<T>
{
    public ValueToken(string name, T value) : base(name)
    {
        Item = value;
    }

    double IValueToken.Item => double.CreateChecked(Item);

    T Item { get; }
}

In older versions you would have to have a derived class IntToken : ValueToke<int> which knows how to do the conversion.

Charlieface
  • 52,284
  • 6
  • 19
  • 43
  • Yes this would be great if I was only concerned with basic numeric types. Unfortunately that's not the case. Is there any way of maybe creating a conversion class/wrapper that would return the Value no matter what it's type, and without reflection? Everything I've tried requires supplying the generic types. If that's what I'm stuck with then I may as well ditch the generics and do pattern matching everywhere. – DaveDod Apr 04 '23 at 23:08
  • Well how do you propose to convert any random `RandomClass` into the one you want? Unless you have a way of defining such a conversion then it can't be done. – Charlieface Apr 05 '23 at 01:52