C# - 检查泛型类型约束中的编译级参数 | ... 哪里 T : new( Foo a )

C# - Check at compilation level arguments in the generic type constraints | ... where T : new( Foo a )

泛型类型中众所周知的约束是 new(),可以添加类似 (args,args,...) 的参数来强制编译器检查 class 是否包含特定构造函数?

方块给你看情况。

public class FooType
{
    public FooType(int arg1)
    { 
    
    }
}

public sealed class FooTypeChildA : FooType
{
    public FooTypeChildA(int a) : base(a)
    { 
    
    }
}

public sealed class FooTypeChildB : FooType
{
    public FooTypeChildB() : base(0)
    { 
    
    }
}

//public class FooConstraint<T> where T : FooType , new() // SUCCESS
//public class FooConstraint<T> where T : FooType , new(int) // ERROR

public class FooConstraint<T> where T : FooType // usually the constraint is "new()", but I need something like this: new(int) that the compiler verify the CHECK_1
{
    
}

public sealed class Passed : FooConstraint<FooTypeChildA> //[CHECK_1] Pass the constraint.
{

}

public sealed class NotPassed : FooConstraint<FooTypeChildB> //[CHECK_1] Not Pass the constraint. 
{

}

这条指令显示了可能的语法异常, new(int arg1)

通用实例调用构造函数的方式并不重要,因为基本反射在 运行 时解决了问题,但想法是强制编译器错误。


ATTEMPT#1 - 这是不可能的;因为接口不能指定构造函数。

public interface IFoo
{
    IFoo(int a); // Error here CS0526
}

ATTEMPT#2 - 最初的问题已关闭,因为版主专注于在 运行time 中解决问题level 而不是 compilation-time level.

This Question is not duplicated in this question: 因为 (T)Activator.CreateInstance( typeof(T) , args ) 不是当您需要非常严格的编译检查时的一个选项。很明显,Activator 没有实现基本原则(必须使用派生 classes 和继承的行为,而不是他们的最终实现 - Liskov)保证 Child Type即使在编译级别也具有 父类型 的行为。


The very-well known constraint in generic types is new(), it is possible to add parameters like (args,args,...) to force the compiler to check that the class contains a particular constructor?

虽然感觉这应该可用,但事实并非如此。您只能为无参数构造函数添加约束。

作为替代方案,请考虑传递工厂函数。在这里,您可以指定输入参数。

public class FooConstraint<T> where T : FooType
{
    private readonly T _myObject;

    public FooConstraint(Func<int, T> generator)
    {
        _myObject = generator(123);
    }
}

注意:您还可以存储 Func 本身,而不是立即使用它并丢弃它。这只是一个例子。

由于 FooConstraint class 定义明确定义了 Func<> 泛型参数,您可以确保 FooConstraint 的使用者确切知道您的 FooConstraint 将用于实例化一个 T 实例。

var myConstraint = new FooConstraint<FooTypeChildA>(myInt => new FooTypeChildA(myInt));

因为你总是只是调用一个构造函数而从不对 Func 做任何花哨的事情,这感觉有点像样板,但考虑到没有真正的通用参数化构造函数约束,这是最接近的选择。