如何根据 class 上的泛型类型在泛型方法上约束类型

How can I constrain a type on a generic method based on a generic type on the class

我正在尝试实现一个泛型方法,但需要将一个泛型类型(T1 - 在 class 上定义)的对象转换为另一个泛型类型(在该方法上定义的 T2)。除了在 class 上定义 T2(我宁愿不要,因为它并不总是需要),有没有办法实现这个?

我正在寻找这样的东西:

public class SomeClass<T1>
{
    public void SomeMethod<T2>(T1 someParameter) where T1 : T2
    {
        T2 someVariable = (T2) someParameter;
    }
}

似乎约束只会以错误的方式起作用,即 where T2:T1 起作用(但显然对我的目的来说是错误的),但 where T1:T2 不起作用。

更新 我需要将 T1 转换为 T2 的原因是我在数据库插入方法中使用结果,该方法使用接口上的反射来确定要插入哪些列。例如,该接口用于防止尝试插入计算列。所以 T2 将是这个接口,而 T1 将是一个原始对象(它会有更多的字段)。因此转换 T2:T1 是不正确的。

听起来你想确保 T2 以某种方式可转换或转换为 T1,你可能想考虑在两个对象之间使用公共接口作为约束SomeMethod.

这是一个简短的例子:

public interface ISomeInterface
{

}

public class SomeBaseClass : ISomeInterface
{

}

public class SomeClass<T1> where T1 : SomeBaseClass
{
    public void SomeMethod<T2>(T1 someParameter) where T2 : ISomeInterface
    {
        if (someParameter is T2 commonInterfacedObject)
        {
            T2 someVariable = commonInterfacedObject;
        }
    }
}

您可以使用 is(或 as)关键字很好地做到这一点 - T2 唯一必要的约束是它是 class

public class SomeClass<T1>
{
    public void SomeMethod<T2>(T1 someParameter) where T2 : class
    {
        if(!(someParameter is T2 t2))
        {           
            throw new Exception("Invalid type");
        }
        Console.WriteLine($"Hello from {t2}");
    }
}

实例:https://dotnetfiddle.net/C5Brhn

(* 很好,虽然它是运行时检查,而不是编译时检查你的原始问题提示)

借助 .NET5,您可以使用更好的表达式 is not

public class SomeClass<T1>
{
    public void SomeMethod<T2>(T1 someParameter) where T2 : class
    {
        if(someParameter is not T2 t2)
        {           
            throw new Exception("Invalid type");
        }
        Console.WriteLine($"Hello from {t2}");
    }
}

请注意,基于此评论

T2 is a subset of the fields on T1 so it is T1 that derives from T2

我用过 T1=Lion 和 T2=Animal

因此,您需要一个基于 T1 的类型。您不能直接执行此操作,但可以执行以下操作,但在使用该方法时必须通过指定类型参数来证明关系。可能还有一些方法可以打破约束。

基本上,引入一个“是”T1 的类型参数,然后您可以引入一个作为该类型基类的类型参数。这最终看起来像这样:

public class SomeClass<TOriginal> {
   public void SomeMethod<TSubstitute, TBase>(
      TOriginal someParameter
   ) where TSubstitute : TOriginal, TBase {
        TBase someVariable = (TBase)(TSubstitute)someParameter;

        ...
    }
}

使用的时候,TOriginalTSubstitute通常是同一个类型。这并没有明确地说 TOriginalTBase 的后代,但它确实说存在一些其他类型 TSubstitute 是两者的后代(并且 'descended from' 包括作为同类型)。