C# PLY文件行的更通用的TryParse()

C# PLY文件行的更通用的TryParse(),c#,parsing,inheritance,architecture,C#,Parsing,Inheritance,Architecture,我正在构建一个导入函数,以便将导入加载到我的程序中。我不确定的部分是面部解析器的实现。这样的面可能有两种不同的版本,面3(连接3个顶点)或面4(连接4个顶点)。在*.ply文件中,它们可能如下所示: 面3: 30123 第4面: 40123 我为它们创建了一个类,实现了接口IFace。但是,接口无法定义方法TryParse(),因为我希望它是静态的。因此,每个Face类都实现了自己的TryParse方法和一个附加的TryParse来返回IFace,而不是Face3或Face4。从几点来看,这些方

我正在构建一个导入函数,以便将导入加载到我的程序中。我不确定的部分是面部解析器的实现。这样的面可能有两种不同的版本,面3(连接3个顶点)或面4(连接4个顶点)。在*.ply文件中,它们可能如下所示:

面3:

30123

第4面:

40123

我为它们创建了一个类,实现了接口
IFace
。但是,接口无法定义方法
TryParse()
,因为我希望它是静态的。因此,每个
Face
类都实现了自己的
TryParse
方法和一个附加的
TryParse
来返回
IFace
,而不是
Face3
Face4
。从几点来看,这些方法基本相同。目前
TryParse
的实现方式(两个不同类中的两个方法)感觉非常笨拙。有没有更好的办法解决这个问题

这就是我目前使用它的方式:

foreach (string line in faceLines)
{
    IFace face = new Face3();              // ugly: I need to instanciate the face bevore I can use "out face"
    var segments = lines[i].Split(' ');    // ugly: I'd rather not touch the line at all. TryParse should do everything

    switch (segments[0])                   // ugly! 
    {
        case "3":
            if (Face3.TryParse(lines[i], out face)) faces.Add((Face4)face);             
            break;
        case "4":
            if (Face4.TryParse(lines[i], out face)) faces.Add((Face4)face);    
            break;
    }
}
下面是
IFace
Face3
Face4

public interface IFace
{
    string ToString();        
}       

public struct Face3 : IFace
{
    public int V1;
    public int V2;
    public int V3;

    public override string ToString()
    {
        return string.Format("3 {0} {1} {2}", V1, V2, V3);
    }

    internal static bool TryParse(string Input, out IFace Face)
    {
        Face3 face = new Face3();
        bool b = TryParse(Input, out face);

        Face = (IFace)face;
        return b;
    }

    internal static bool TryParse(string Input, out Face3 Face)
    {
        Face = new Face3();

        var args = Input.Split(' ');
        if (args.Length != 4) return false;

        bool success = true;

        success = success && int.TryParse(args[1], out Face.V1);
        success = success && int.TryParse(args[2], out Face.V2);
        success = success && int.TryParse(args[3], out Face.V3);

        if (!success) return false;
        return true;
    }
}       

public struct Face4 : IFace
{
    public int V1;
    public int V2;
    public int V3;
    public int V4;

    public override string ToString()
    {
        return string.Format("4 {0} {1} {2} {3}", V1, V2, V3, V4);
    }

    internal static bool TryParse(string Input, out IFace Face)
    {
        Face4 face = new Face4();
        bool b = TryParse(Input, out face);

        Face = (IFace)face;
        return b;
    }

    internal static bool TryParse(string Input, out Face4 Face)
    {
        Face = new Face4();

        var args = Input.Split(' ');
        if (args.Length != 5) return false;

        bool success = true;

        success = success && int.TryParse(args[1], out Face.V1);
        success = success && int.TryParse(args[2], out Face.V2);
        success = success && int.TryParse(args[3], out Face.V3);
        success = success && int.TryParse(args[4], out Face.V4);

        if (!success) return false;
        return true;
    }
}
更新:根据@Heslacher的回答,我实施了一些更改。由于我希望能够直接调用
Face.TryParse
,而不需要
FaceParser
类,因此我将接口
IFace
更改为抽象类
Face
。因此,
Face3
Face4
不再是
struct
,而是
class
。我对这个解决方案很满意

解析面:

Face face;
if (Face.TryParse(lines[i], out face))
{
    faces.Add(face);
}
与:


免责声明:所以这更像是一个代码审查(您已将问题删除到早期),但它应该解决您的问题


根据输入参数,应使用
camelCase
套管命名


我看不出有什么原因,为什么您为
Face3
Face4
重载
TryParse()
方法

通过设置传递的输入参数
face
(命名准则)=
null
,可以提前返回,而无需为参数指定任何新对象

如果
TryParse()
返回
false
,则以创建新的
Face3
的方式,传入的
IFace
也将被初始化

这个

public struct Face3 : IFace
{
    public int V1;
    public int V2;
    public int V3;

    public override string ToString()
    {
        return string.Format("3 {0} {1} {2}", V1, V2, V3);
    }

    internal static bool TryParse(string input, out IFace face)
    {

        face = null;

        var args = input.Split(' ');
        if (args.Length != 4)
        {
            return false;
        }

        Face3 currentFace = new Face3();
        bool success = true;
        success=success && int.TryParse(args[1], out currentFace.V1);
        success = success && int.TryParse(args[2], out currentFace.V2);
        success = success && int.TryParse(args[3], out currentFace.V3);

        if (!success)
        {
            return false;
        }
        face = currentFace;
        return true;
    }

}  
只会工作,也没有这个


通过添加一个可以是私有的
FaceParser
类和一个静态
TryParse()
方法,如

private class FaceParser
{
    public static bool TryParse(string line, out IFace face)
    {
        if (Face3.TryParse(line, out face)) { return true; }
        if (Face4.TryParse(line, out face)) { return true; }
        return false;
    }
}  
如果它是私有的,则应该包含在添加到面的类中,您的初始示例可以简化为如何解析行

foreach (string line in faceLines)
{
    IFace face; 
    if (FaceParser.TryParse(line, out face))
    {
        faces.Add(face);
    }
}

您似乎只将
IFace
接口用作标记接口。向接口添加
ToString()
方法在某种程度上是多余的,因为每个对象已经包含一个可重写的
ToString()
方法。因此,您可以简化您的界面以

public interface IFace {} 
一个普通的解析器将处理所有的令牌,从第一个开始,让控制流由读取的令牌决定

在您的例子中,(全局)解析器将读取第一个int,然后决定是否继续使用代码的“face3”或“face4”部分,同时读取更多int,并在行尾返回一个
face3
face4
实例

因此,这种解析器的一般概述如下:

getNextToken();
switch(currentToken)
{
    case Face3Start:
        getface3();
        break;
    case Face4Start:
        getface4();
        break;
}

谢谢你的回答-我从代码评审中删除了这个问题,因为我认为这是一个更具体的问题,而不是一般的要求评审。。。我仍然不能完全确定我应该把它放在哪里。如果有问题的代码正在运行并返回预期的结果,而您没有要求添加新的功能,那么它将在代码审阅时讨论。这听起来像是一个很好的经验法则-我会记住它!
public interface IFace {} 
getNextToken();
switch(currentToken)
{
    case Face3Start:
        getface3();
        break;
    case Face4Start:
        getface4();
        break;
}