英文:
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<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(_o + translation, _r);
    public T MoveTo<T>(in v2 position) => new(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.
答案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<T> : IGeometry where T : IGeometry<T>
{
	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<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);
}
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<Circle>
You need to be careful not to.
答案2
得分: 0
接口本身应该是通用的,而不是方法本身,如下所示:
```cs
public interface IGeometry<T> where T : IGeometry<T>
{
    T Translate(in v2 vector);
    T MoveTo(in v2 vector);
}
然后,您的类型将使用指定的通用类型实现接口:
public readonly struct Circle : IGeometry<Circle>
您可以在BCL的INumber<T>接口中看到这一点,该接口在所有数字上都实现了: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<T> where T : IGeometry<T>
{
    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<Circle>
You can see this in the BCL's INumber<T> interface, which is implemented on all numbers: https://learn.microsoft.com/en-us/dotnet/api/system.numerics.inumber-1?view=net-7.0
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论