简体   繁体   中英

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

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 .

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...

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM