C# PLY文件行的更通用的TryParse()
我正在构建一个导入函数,以便将导入加载到我的程序中。我不确定的部分是面部解析器的实现。这样的面可能有两种不同的版本,面3(连接3个顶点)或面4(连接4个顶点)。在*.ply文件中,它们可能如下所示: 面3: 30123 第4面: 40123 我为它们创建了一个类,实现了接口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。从几点来看,这些方
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;
}