简体   繁体   中英

Compile-time type constraint for run-time type

I want to be able to use the System.Type with a given constraint at compile-time;

Is there an elegant way to solve this problem?

internal abstract class BaseClass {}
internal class SubClass : BaseClass {}
internal class OtherClass {}

internal class Consumer
{
    public void DoSomething(Type pType) {}
    public void DoSomething(BaseClass pBaseClass) {}
    public void DoSomething<tBaseClass>(tBaseClass pBaseClass) where tBaseClass : BaseClass {}
}

[TestFixture()]
public class TypeConstraintTest
{
    [Test()]
    public void TestCase1()
    {
        var lConsumer = new Consumer();

        lConsumer.DoSomething(typeof (SubClass));
        // This should not be allowed. Should have a type constraint.
        lConsumer.DoSomething(typeof (OtherClass));

        lConsumer.DoSomething(null as SubClass);
        // This will generate a compiler error, but it's
        // not an elegant solution, not easily readable/understandable.
        lConsumer.DoSomething(null as OtherClass);
    }
}

Hope this other example helps to clarify my intentions (Apologies if it is unclear as I had to write it quickly):

[TestFixture()]
public class ExampleTest
{
    internal interface GroupingInterface {}
    internal interface TargetInterface {}
    internal class Class1 : GroupingInterface, TargetInterface {}
    internal class Class2 : GroupingInterface {}

[Test()]
void TestCase()
{
    var lGroup = new List<GroupingInterface>() { new Class1(), new Class2() };

    foreach(var lClass in lGroup)
    {
        this.TestMethod(lClass.GetType());

        // This works, but we are passing the object just for forcing the type.
        // We are not going to use the object itself, so it would be better not
        // To pass the reference to the specific object if there is a way...
        this.TestMethodWithInstance(lClass);

        // Don't know the type at compile-time as it is variable.
        //this.TargetMethodWithGeneric<???>

        // Ideally, there should be something like a generic "variable" method:
        //this.TargetMethodWithGeneric<typeFrom(lClass)>
        // This should give a compiler error as a GroupingInterface is not a TargetInterface.
        // But, if we pass any TargetInterface subtype it should compile.
    }
}

void TestMethod(Type pType)
{
    // At this point, we want to make sure pType is
    // a subtype of TargetInterface at compile-time.

    // SHOULD NOT BE AT RUNTIME, SHOULD NOT COMPILE IF WRONG TYPE PASSED:
    if (pType.GetInterfaces().Contains(typeof (TargetInterface))) throw new Exception();
}

void TestMethodWithInstance(TargetInterface pClass)
{
    var lSubType = pClass.GetType();

    // Do something with the type...
}

void TargetMethodWithGeneric<tType>() where tType : TargetInterface
{
    // Do something with tType.
}
}

There isn't really a way to do what you are asking at compile time. System.Type is the type expected and given however, with your generic I would argue that it is exactly the solution you want. Typically you wouldn't pass null in directly to a method anyway, it would be a variable of some type removing the requirement of casting. If what you are trying to do doesn't actually take a parameter but just a type you could change the definition to better match.

public void DoSomething<tBaseClass>() where tBaseClass : BaseClass 
{
}

Then the caller need only specify the type.

lConsumer.DoSomething<OtherClass>();

Edit

I am still not understanding the need for something other than what you have. Even if it is a list of some other type, you can just use Linq to filter it to the type you are interested in using OfType<T>() .

[Test()]
public void TestCase()
{
    var lGroup = new List<GroupingInterface>() { new Class1(), new Class2() };

    // If you know you need to extract classes of a certain type you can use this:
    foreach (var lclass in lGroup.OfType<TargetInterface>())
    {
        // using OfType means lclass is already cast as the type expected, if the object is not of that type it will not be iterated
        TestMethodWithInstance(lclass);
    }        
}  

Are you perhaps looking for the runtime check:

if( !typeof(BaseClass).IsAssignableFrom(t) )
    throw new Exception("Must be BaseClass derivative");

Why are you wanting to use generics as opposed to just defining the interface or base class you are passing in? There is no reason to use generics when you want specific types.

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