是否可以为类型参数指定一个通用约束,以便从另一个类型转换?

fos*_*ndy 9 c# generics

假设我用以下内容编写了一个库:

public class Bar { /* ... */ }

public class SomeWeirdClass<T>
    where T : ???
{
    public T BarMaker(Bar b)
    {
        // ... play with b
        T t = (T)b
        return (T) b;
    }
}
Run Code Online (Sandbox Code Playgroud)

后来,我希望用户通过定义他们自己的类型来使用我的库,这些类型可以转换为Bar并使用SomeWeirdClass"factory".

public class Foo
{
    public static explicit operator Foo(Bar f)
    {
        return new Bar();
    }
}

public class Demo
{
    public static void demo()
    {
        Bar b = new Bar();
        SomeWeirdClass<Foo> weird = new SomeWeirdClass<Foo>();
        Foo f = weird.BarMaker(b);
    }
}
Run Code Online (Sandbox Code Playgroud)

这将编译,如果我设置,where T : Foo但问题是我不知道在库的编译时Foo,我实际上想要更像where T : some class that can be instantiated, given a Bar

这可能吗?从我有限的知识来看,它似乎并不是,但.NET框架及其用户的独创性总让我惊喜......

这可能与静态接口方法的概念有关- 至少,我可以看到能够指定工厂方法的存在以创建对象的价值(类似于您已经可以执行的方式where T : new())

编辑:解决方案 - 感谢Nick和bzIm - 对于其他读者,我将提供一个完整的解决方案,因为我理解它:edit2:此解决方案需要Foo公开一个公共默认构造函数.对于一个甚至更愚蠢的更好的解决方案,不需要这个看到这篇文章的最底层.

public class Bar {}

public class SomeWeirdClass<T>
    where T : IConvertibleFromBar<T>, new()
{
    public T BarMaker(Bar b)
    {
        T t = new T();
        t.Convert(b);
        return t;
    }
}

public interface IConvertibleFromBar<T>
{
    T Convert(Bar b);
}

public class Foo : IConvertibleFromBar<Foo>
{
    public static explicit operator Foo(Bar f)
    {
        return null;
    }

    public Foo Convert(Bar b)
    {
        return (Foo) b;
    }
}

public class Demo
{
    public static void demo()
    {
        Bar b = new Bar();
        SomeWeirdClass<Foo> weird = new SomeWeirdClass<Foo>();
        Foo f = weird.BarMaker(b);
    }
}
Run Code Online (Sandbox Code Playgroud)

edit2:解决方案2:创建一个类型转换器工厂使用:

#region library defined code

public class Bar {}

public class SomeWeirdClass<T, TFactory>
    where TFactory : IConvertorFactory<Bar, T>, new()
{
    private static TFactory convertor = new TFactory();

    public T BarMaker(Bar b)
    {
        return convertor.Convert(b);
    }
}

public interface IConvertorFactory<TFrom, TTo>
{
    TTo Convert(TFrom from);
}

#endregion

#region user defined code

public class BarToFooConvertor : IConvertorFactory<Bar, Foo>
{
    public Foo Convert(Bar from)
    {
        return (Foo) from;
    }
}

public class Foo
{
    public Foo(int a) {}

    public static explicit operator Foo(Bar f)
    {
        return null;
    }

    public Foo Convert(Bar b)
    {
        return (Foo) b;
    }
}

#endregion

public class Demo
{
    public static void demo()
    {
        Bar b = new Bar();
        SomeWeirdClass<Foo, BarToFooConvertor> weird = new SomeWeirdClass<Foo, BarToFooConvertor>();
        Foo f = weird.BarMaker(b);
    }
}
Run Code Online (Sandbox Code Playgroud)

Eri*_*ert 6

听起来您找到了解决更大问题的解决方案。要回答您的特定问题:不,C#和CLR都不支持“向后”通用类型参数约束。那是,

class C<T> where Foo : T
Run Code Online (Sandbox Code Playgroud)

不支持“ T必须是Foo或Foo转换为的类型”。

有些语言有这种约束。IIRC Scala是这种语言。我怀疑此功能对于逆接口的某些用法会很方便。


Nic*_*ick 4

我认为语言中不一定有一种语法上很酷的方法来做到这一点。解决您的问题的一种可能的解决方案是定义一个可转换接口:

public interface IConvertible<T>
    where T :  new()   // Probably will need this
{
    T Convert();
}
Run Code Online (Sandbox Code Playgroud)

那么你的课程可能是:

public class Foo : IConvertible<Bar>
{
}
Run Code Online (Sandbox Code Playgroud)

我认为这让你接近你想要的地方......你的问题中的所有 Foo 和 Bar 有时会让你很难准确地确定你的意图是什么。希望这可以帮助。

编辑:添加了 where 约束...您可能必须能够在可转换类中创建一个新实例。

编辑2:使 Foo 继承自ICovertible<Bar>