返回具有不同泛型类型的泛型接口实现

时间:2020-04-10 12:02:10

标签: c# generics compiler-errors

我已经创建了这个简单的通用接口:

public interface IInitializerSettings<in ViewerType> where ViewerType : Component
{
    void Apply(ViewerType dataViewer);
}

并为其添加了一个实现:

public class MenuSettings : IInitializerSettings<CustomGridLayout>
{
    public void Apply(CustomGridLayout dataViewer)
    {
        Debug.Log("Applied");
    }
}

public class CustomGridLayout : CustomLayout
{
    // The implementation code
}

现在,我尝试像这样使用它:

public IInitializerSettings<CustomLayout> GetDefaultSettings()
{
    return new MenuSettings();
}

但是我收到此错误“无法将类型MenuSettings转换为返回类型IInitializerSettings”

我不明白为什么不允许这样做,CustomGridLayout继承了CustomLayout

我只能找到this question,但是这种解决方案对我不起作用(我不能使用out关键字)。

1 个答案:

答案 0 :(得分:3)

之所以不能这样做,是因为对于一个互逆接口(通过对通用类型参数使用in指定),您不能将其隐式转换为派生类型较少的实例。如果您以IEnumerable<T>(协变)和Action<T>(相反)的角度来看,我认为要点in the docs可以解释得很好。

正如塞尔文在评论中提到的,Apply中的MenuSettings方法期望一个CustomGridLayout的实例,因此无法尝试将MenuSettings转换为IInitializerSettings<CustomLayout>因为public void Apply(CustomGridLayout dataViewer)无法处理CustomLayout作为输入。让我举个例子:

public class CustomLayout
{
    public void SetupCustomLayout() { ... }
}

public class CustomGridLayout : CustomLayout
{
    public void SetupGrid() { ... }
}

public class MenuSettings : IInitializerSettings<CustomGridLayout>
{
    public void Apply(CustomGridLayout dataViewer)
    {
        dataViewer.SetupGrid();
    }
}


// Later in the code...

var menuSettings = new MenuSettings();

// This cast is what GetDefaultSettings() is trying to do
var genericSettings = (IInitializerSettings<CustomLayout>)menuSettings;

var layout = new CustomLayout();

// Looking at the type of 'genericSettings' this following line should be possible
// but 'MenuSettings.Apply()' is calling 'dataViewer.SetupGrid()' which doesn't exist
// in 'layout', so 'layout' is not a valid input
genericSettings.Apply(layout);

因此,相对于the docs,您已经将IInitializerSettings<ViewerType>定义为一个协变接口,但是正试图将其用作协变接口-这是不可能的。