C# Access to a generic class member whose type is a generic argument type, only via an interface

StackOverflow https://stackoverflow.com/questions/20654338

  •  19-09-2022
  •  | 
  •  

Question

I have a generic class who holds a member whose type is an argument type.
I want to be able to access this member only by one of the interface it implements.
The reason I want to access the member only via this interface, instead of enumerating all the concrete types it could be, is because there are a great number of those types.

So concretely I want to find an equivalent of line 61 in that code (it is a compilation error):

using System;
using System.Linq;

/* Interfaces */
public interface IArgumentClass
{
    void IArgumentClassMethod();
}
public interface ISpecialArgumentClass
{
    void ISpecialArgumentClassMethod();
}
public interface IContainerClass
{
    void IContainerClassClassMethod();
}

/* Argument types */
public class ArgumentClass0 : IArgumentClass
{
    public void IArgumentClassMethod(){}
}
public class SpecialArgumentClass0 : IArgumentClass, ISpecialArgumentClass
{
    public void IArgumentClassMethod(){}
    public void ISpecialArgumentClassMethod(){}
}
public class SpecialArgumentClass1 : IArgumentClass, ISpecialArgumentClass
{
    public void IArgumentClassMethod() { }
    public void ISpecialArgumentClassMethod() { }
}

/* Container types */
public class GenericContainer<T> : IContainerClass
    where T : IArgumentClass, new()
{
    public T t = new T();
    public void IContainerClassClassMethod() { }
}
public class NonGenericContainer : IContainerClass
{
    public void IContainerClassClassMethod(){}
}

/* main program */
public class Test
{
    public static void Main()
    {
        // Instantiate
        IContainerClass[] containers = 
        {
            new GenericContainer<ArgumentClass0>(),
            new GenericContainer<SpecialArgumentClass0>(),
            new GenericContainer<SpecialArgumentClass1>(),
            new NonGenericContainer()
        };

        // We want to call IContainerClassClassMethod methods on all instances:
        foreach (IContainerClass container in containers)
            container.IContainerClassClassMethod();

        // We want to call ISpecialArgumentClassMethod on instances where it's possible:
        foreach (IContainerClass container in containers)
        {
            if (container.GetType().IsGenericType && container.GetType().GetGenericTypeDefinition() == typeof(GenericContainer<>))
            {
                foreach (Type typeArgument in container.GetType().GetGenericArguments())
                {
                    if (typeArgument.GetInterfaces().Contains(typeof(ISpecialArgumentClass)))
                    {
                        // Next line is a compilation error. How can I get a similar result?
                        GenericContainer<ISpecialArgumentClass> mySpecializedClassWithSpecialArgument = container as GenericContainer<ISpecialArgumentClass>;
                        mySpecializedClassWithSpecialArgument.t.ISpecialArgumentClassMethod();
                    }
                }
            }
        }
}
}

Note: You can fork and edit the code here.

Was it helpful?

Solution 2

A really simple solution is to just cast it to dynamic - you know it has a t field, so this should be safe to do.

if (typeArgument.GetInterfaces().Contains(typeof(ISpecialArgumentClass)))
{
    dynamic mySpecializedClassWithSpecialArgument =
        mySpecializedClass as dynamic;

    ISpecialArgumentClass specialArgumentClass = mySpecializedClassWithSpecialArgument.t;

    Console.WriteLine(specialArgumentClass.GetSpecialString());
}

Note

I tried to edit it in ideone, but it would not compile. I suspect it's targeting an older version of .NET - dynamic was introduced in .NET 4 (VS 2010). However, I've tested the code in 2013 and it works.

OTHER TIPS

You get the compilation error because ISpecialArgumentClass is not of type IArgumentClass, but your GenericClass requires exactly this.

To solve this, you could introduce an empty interface which serves as base for both argument classes:

First, modify your interface declaration like this:

public interface IArgumentClassBase
{
}

public interface IArgumentClass : IArgumentClassBase
{
    String GetNormalString();
}

public interface ISpecialArgumentClass : IArgumentClassBase
{
    String GetSpecialString();
}

... then modify your generic class declaration like so:

public class GenericClass<T> : IContainerClass
    where T : IArgumentClassBase, new()

Then the rest of your code should work...

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top