如何修复”目标运行时不支持重写中的协变类型?”

huangapple go评论72阅读模式
英文:

How to fix "Target runtime doesn't support covariant types in overrides?

问题

I understand that you're looking for a translation of the provided code and the associated problem. Here's the translation:

我有一个Builder,让我们看看代码:
```csharp
public abstract class UISchemaBuilderBase
{
   protected readonly UISchemaSpecification UiSchemaSpecification;

   public UISchemaSpecification Builder() =>
      UiSchemaSpecification;

   public virtual UISchemaBuilderBase AddBoolean(
       string fieldName,
       string groupName = null)
   {
       .. 一些逻辑
    
       UiSchemaSpecification.Properties[ fieldName ] = schema;
       return this;
   }
}
public class UISchemaBuilder : UISchemaBuilderBase
{
    public UISchemaBuilder() : base(new UISchemaSpecification()) { }

    public UISchemaGroupBuilder StartGroup(string name)
    {
        UiSchemaSpecification.ParsecMetadata ??= new();
        UiSchemaSpecification.ParsecMetadata.Groups ??= new();
        UiSchemaSpecification.ParsecMetadata.Groups.Add( new(name) );

        return new UISchemaGroupBuilder(name, this, UiSchemaSpecification);
    }
}
public class UISchemaGroupBuilder : UISchemaBuilderBase
{
    private readonly string _name;
    private readonly UISchemaBuilder _schemaBuilder;

    public UISchemaGroupBuilder(
       string name,
       UISchemaBuilder schemaBuilder,
       UISchemaSpecification specification)
       : base(specification)
    {
        _name = name;
        _schemaBuilder = schemaBuilder;
    }

   public override UISchemaGroupBuilder AddBoolean(
       string fieldName,
       string fieldTitle,
       string groupName = null)
    {
        base.AddBoolean(fieldName, fieldTitle, _name);
        return this;
    }

    public UISchemaBuilder EndGroup() =>
        _schemaBuilder;
}

因此,实现的代码允许执行以下操作:

var builder = new UISchemaBuilder()
  .StartGroup("groupName")
    .AddBoolean()
  .EndGroup()
  .StartGroup("anotherGroupName")
    .AddBoolean()
  .EndGroup()
  .Build();

问题是:在netstandard中我不能做同样的事情,我不明白如何解决这个问题...
我应该更改UISchemaGroupBuilder中方法的返回类型。例如,AddText应该返回UISchemaBuilderBase,而不是UISchemaGroupBuilder
但是如果我按照这些建议去做,我的构建器就无法工作。我不能在AddBoolean之后使用EndGroup,例如。
有人可以帮助我吗?请。

英文:

I have Builder, let's see code:

public abstract class UISchemaBuilderBase
{
   protected readonly UISchemaSpecification UiSchemaSpecification;

   public UISchemaSpecification Builder() =>
      UiSchemaSpecification;

   public virtual UISchemaBuilderBase AddBoolean(
       string fieldName,
       string groupName = null)

   {
       .. some logic
    
       UiSchemaSpecification.Properties[ fieldName ] = schema;
       return this;
   }
}
public class UISchemaBuilder : UISchemaBuilderBase
{
    public UISchemaBuilder() : base(new UISchemaSpecification()) { }

    public UISchemaGroupBuilder StartGroup(string name)
    {
        UiSchemaSpecification.ParsecMetadata ??= new();
        UiSchemaSpecification.ParsecMetadata.Groups ??= new();
        UiSchemaSpecification.ParsecMetadata.Groups.Add( new(name) );

        return new UISchemaGroupBuilder(name, this, UiSchemaSpecification);
    }
}
public class UISchemaGroupBuilder : UISchemaBuilderBase
{
    private readonly string _name;
    private readonly UISchemaBuilder _schemaBuilder;

    public UISchemaGroupBuilder(
       string name,
       UISchemaBuilder schemaBuilder,
       UISchemaSpecification specification)
       : base(specification)
    {
        _name = name;
        _schemaBuilder = schemaBuilder;
    }

   public override UISchemaGroupBuilder AddBoolean(
       string fieldName,
       string fieldTitle,
       string groupName = null)
    {
        base.AddBoolean(fieldName, fieldTitle, _name);
        return this;
    }

    public UISchemaBuilder EndGroup() =>
        _schemaBuilder;
}

So implemented code allows to do this:

var builder = new UISchemaBuilder()
  .StartGroup("groupName")
    .AddBoolean()
  .EndGroup()
  .StartGroup("anotherGroupName")
    .AddBoolean()
  .EndGroup()
  .Build();

The problem is: I can't do the same in netstandard and I don't understand how to fix this..
I should change return type of methods in UISchemaGroupBuilder. For example AddText should return UISchemaBuilderBase, not UISchemaGroupBuilder.
But if I follow by these advices, my builder wouldn't work. I can't use EndGroup after AddBoolean for example.
Can someone help me, please?

答案1

得分: 2

根据Microsoft关于C#版本的这篇文章

> .NET Standard 2.1 C# - 8.0
>
> .NET Standard 2.0 C# - 7.3

covariant return types是C# 9的一部分,因此与.NET Standard不兼容。

基本上,覆盖方法中的协变返回类型需要与以前的语言版本相比在CLR中进行更改,因此它与.NET Framework和.NET Standard不兼容。

在OP示例中,组构建器的AddBool方法可以是非虚拟方法,只是一个重载方法,它隐藏了基类层次结构中的方法:

public class UISchemaGroupBuilder : UISchemaBuilderBase
{
    ...
    public new UISchemaGroupBuilder AddBoolean(
       string fieldName,
       string fieldTitle,
       string groupName = null)
    {
        base.AddBoolean(fieldName, fieldTitle, _name);
        return this;
    }
}
英文:

As per this article about C# versions from Microsoft:

> .NET Standard 2.1 C# - 8.0
>
> .NET Standard 2.0 C# - 7.3

But covariant return types is part of C# 9, thus it is not compatible with .Net Standard.

Basically covariant return types in override methods require changes in CLR in comparison with previous language version, so it is incompatible with in .Net Framework and .Net Standard.

In OP example, AddBool method of group builder could be a non-virtual method, just an overloaded one which hides method in the base class hierarchy:

public class UISchemaGroupBuilder : UISchemaBuilderBase
{
    ...
    public new UISchemaGroupBuilder AddBoolean(
       string fieldName,
       string fieldTitle,
       string groupName = null)
    {
        base.AddBoolean(fieldName, fieldTitle, _name);
        return this;
    }

答案2

得分: 2

由于协变返回类型在 C# 9 之前不可用,您可以改为使用泛型。例如,简化版本的代码可以将生成器基类变为泛型并添加约束:

public abstract class UISchemaBuilderBase<TSchemaBuilder>
	where TSchemaBuilder : UISchemaBuilderBase<TSchemaBuilder>
{
	public virtual TSchemaBuilder AddBoolean()
		=> throw new NotImplementedException();
}

现在您的派生类必须指定泛型类型:

public class UISchemaBuilder : UISchemaBuilderBase<UISchemaBuilder>
{
	public override UISchemaBuilder AddBoolean()
		=> throw new NotImplementedException();
}

public class UISchemaGroupBuilder : UISchemaBuilderBase<UISchemaGroupBuilder>
{
	public override UISchemaGroupBuilder AddBoolean()
		=> throw new NotImplementedException();
}
英文:

Since covariant return types aren't available prior to C# 9, you can instead make use of generics. For example, a simplified version of your code, make the builder base generic and give it a constraint:

public abstract class UISchemaBuilderBase&lt;TSchemaBuilder&gt;
	where TSchemaBuilder : UISchemaBuilderBase&lt;TSchemaBuilder&gt;
{
	public virtual TSchemaBuilder AddBoolean()
		=&gt; throw new NotImplementedException();
}

Now your derived classes must specify the generic type:

public class UISchemaBuilder : UISchemaBuilderBase&lt;UISchemaBuilder&gt;
{
	public override UISchemaBuilder AddBoolean()
		=&gt; throw new NotImplementedException();

}

public class UISchemaGroupBuilder : UISchemaBuilderBase&lt;UISchemaGroupBuilder&gt;
{
	public override UISchemaGroupBuilder AddBoolean()
		=&gt; throw new NotImplementedException();
}

huangapple
  • 本文由 发表于 2023年7月20日 21:02:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/76730192.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定