如何实现具有定义返回类型的通用接口

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

How to implement a generic interface with defined return types

问题

I have an interface for my geometric shapes that looks like this:

public interface IGeometry
{
    T Translate<T>(in v2 vector) where T : IGeometry;
    T MoveTo<T>(in v2 vector) where T : IGeometry;
}

I am confused how I define this in my structs where I specifically return the struct type that implements the interface. I have this in my Circle : IGeometry struct:

public readonly struct Circle : IGeometry
{
    public readonly v2 _o;
    public readonly double _r, _area, _circum;
    public v2 Origin => _o;
    public double Radius => _r;
    public double Circumference => _circum;

    public Circle(in v2 origin, in double radius)
    {
        _o = origin;
        _r = radius;
        _area = _r * _r * Math.PI;
        _circum = _r * 2 * Math.PI;
    }

    public T Translate<T>(in v2 translation) => new Circle(_o + translation, _r);
    public T MoveTo<T>(in v2 position) => new Circle(position, _r);
}

The error I get, however, is:

Error CS0425: The constraints for type parameter 'T' of method 'Circle.Translate<T>(in v2)' 
must match the constraints for type parameter 'T' of interface method 
'IGeometry.Translate<T>(in v2)'. Consider using an explicit interface implementation instead.

Not quite sure I understand how to fix this.

英文:

I have an interface for my geometric shapes that looks like this:

public interface IGeometry
{
    T Translate&lt;T&gt;(in v2 vector) where T : IGeometry;
    T MoveTo&lt;T&gt;(in v2 vector) where T : IGeometry;
}

I am confused how i define this in my structs where i specifically return the struct type that implements the interface. I have this in my Circle : IGeometry struct:

public readonly struct Circle : IGeometry
{
    public readonly v2 _o;
    public readonly double _r, _area, _circum;
    public v2 Origin =&gt; _o;
    public double Radius =&gt; _r;
    public double Circumference =&gt; _circum;

    public Circle(in v2 origin, in double radius)
    {
        _o = origin;
        _r = radius;
        _area = _r * _r * Math.PI;
        _circum = _r * 2 * Math.PI;
    }

    public T Translate&lt;T&gt;(in v2 translation) =&gt; new(_o + translation, _r);
    public T MoveTo&lt;T&gt;(in v2 position) =&gt; new(position, _r);
}

The error i get however is:

Error CS0425: The constraints for type parameter &#39;T&#39; of method &#39;Circle.Translate&lt;T&gt;(in v2)&#39; 
must match the constraints for type parameter &#39;T&#39; of interface method 
&#39;IGeometry.Translate&lt;T&gt;(in v2)&#39;. Consider using an explicit interface implementation instead.

Not quite sure I understand how to fix this.

答案1

得分: 3

如果您需要一个具有成员的非泛型版本和泛型版本的接口,那么您确实需要两个接口。这是需要使用方法重写的情况之一。

尝试这些:

public interface IGeometry
{
    IGeometry Translate(in v2 vector);
    IGeometry MoveTo(in v2 vector);
}

public interface IGeometry<T> : IGeometry where T : IGeometry<T>
{
    new T Translate(in v2 vector);
    new T MoveTo(in v2 vector);
}

现在,您可以隐式实现泛型接口,以及显式实现非泛型接口。

public readonly struct Circle : IGeometry<Circle>
{
    private readonly v2 _o;
    private readonly double _r, _area, _circum;
    public v2 Origin => _o;
    public double Radius => _r;
    public double Circumference => _circum;

    public Circle(in v2 origin, in double radius)
    {
        _o = origin;
        _r = radius;
        _area = _r * _r * Math.PI;
        _circum = _r * 2 * Math.PI;
    }

    public Circle Translate(in v2 translation) => new(_o + translation, _r);
    public Circle MoveTo(in v2 position) => new(position, _r);

    IGeometry IGeometry.Translate(in v2 vector) => this.Translate(vector);
    IGeometry IGeometry.MoveTo(in v2 vector) => this.MoveTo(vector);
}

现在,通过这些您可以这样做:

Circle circle1 = new Circle();
Circle circle2 = circle1.Translate(new v2());
IGeometry geometry1 = circle1;
IGeometry geometry2 = geometry1.Translate(new v2());
Circle circle2a = (Circle)geometry2;

对非泛型接口方法的调用会调用泛型版本以保持类型安全。

但是,请谨记,您可以合法地实现这样的代码:

public readonly struct Square : IGeometry<Circle>

您需要小心不要这样做。

英文:

If you require a non-generic with generic versions of the members then you really need two interfaces. This is one of the circumstances where shadowing methods is necessary.

Try these:

public interface IGeometry
{
	IGeometry Translate(in v2 vector);
	IGeometry MoveTo(in v2 vector);
}

public interface IGeometry&lt;T&gt; : IGeometry where T : IGeometry&lt;T&gt;
{
	new T Translate(in v2 vector);
	new T MoveTo(in v2 vector);
}

Now you can implement the generic interface implicitly and the non-generic interface explicitly.

public readonly struct Circle : IGeometry&lt;Circle&gt;
{
	private readonly v2 _o;
	private readonly double _r, _area, _circum;
	public v2 Origin =&gt; _o;
	public double Radius =&gt; _r;
	public double Circumference =&gt; _circum;

	public Circle(in v2 origin, in double radius)
	{
		_o = origin;
		_r = radius;
		_area = _r * _r * Math.PI;
		_circum = _r * 2 * Math.PI;
	}

	public Circle Translate(in v2 translation) =&gt; new(_o + translation, _r);
	public Circle MoveTo(in v2 position) =&gt; new(position, _r);

	IGeometry IGeometry.Translate(in v2 vector) =&gt; this.Translate(vector);
	IGeometry IGeometry.MoveTo(in v2 vector) =&gt; this.MoveTo(vector);
}

Now with all of that you can do this:

Circle circle1 = new Circle();
Circle circle2 = circle1.Translate(new v2());
IGeometry geometry1 = circle1;
IGeometry geometry2 = geometry1.Translate(new v2());
Circle circle2a = (Circle)geometry2;

Calls to the non-generic interface methods call the generic versions to keep type safety.

However, keep in mind that you could implement this legally:

public readonly struct Square : IGeometry&lt;Circle&gt;

You need to be careful not to.

答案2

得分: 0

接口本身应该是通用的,而不是方法本身,如下所示:
```cs
public interface IGeometry&lt;T&gt; where T : IGeometry&lt;T&gt;
{
    T Translate(in v2 vector);
    T MoveTo(in v2 vector);
}

然后,您的类型将使用指定的通用类型实现接口:

public readonly struct Circle : IGeometry&lt;Circle&gt;

您可以在BCL的INumber&lt;T&gt;接口中看到这一点,该接口在所有数字上都实现了:https://learn.microsoft.com/en-us/dotnet/api/system.numerics.inumber-1?view=net-7.0


<details>
<summary>英文:</summary>

The interface itself should be generic, not the methods themselves, like so:
```cs
public interface IGeometry&lt;T&gt; where T : IGeometry&lt;T&gt;
{
    T Translate(in v2 vector);
    T MoveTo(in v2 vector);
}

Then your typed will implement the interface with that generic specified:

public readonly struct Circle : IGeometry&lt;Circle&gt;

You can see this in the BCL's INumber&lt;T&gt; interface, which is implemented on all numbers: https://learn.microsoft.com/en-us/dotnet/api/system.numerics.inumber-1?view=net-7.0

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

发表评论

匿名网友

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

确定