功能样式的实现方法

时间:2013-04-16 21:06:23

标签: c# linq

我有以下方法:

private List<string> CreateSegments(string virtualPath)
{
    List<string> segments = new List<string>();
    int i = virtualPath.IndexOf('/', 1);
    while (i >= 0 && i < virtualPath.Length)
    {
        var segment = virtualPath.Substring(0, i);
        if (!string.IsNullOrWhiteSpace(segment))
        {
            segments.Add(segment);
            segments.Add(VirtualPathUtility.Combine(segment, "default"));
        }
        i = virtualPath.IndexOf('/', i + 1);
    }
    segments.Add(virtualPath);
    segments.Add(VirtualPathUtility.Combine(virtualPath, "default"));

    return segments;
}

基本上,它会创建路径段,我将用它来检查任何这些段中是否存在文件。像这样:

string[] extensions = GetRegisteredExtensions();
HttpServerUtilityBase server = HttpContext.Current.Server;
List<string> segments = CreateSegments(virtualPath);

// check if a file exists with any of the registered extensions
var match = extensions.SelectMany(x => segments.Select(s => string.Format("{0}.{1}", s, x)))
.FirstOrDefault(p => System.IO.File.Exists(server.MapPath(p)));

以上所有代码看起来都可以使用一些清理和优化,但我正在寻找一种方法来使用LINQ,如果可能的话,生成段。

类似于:var segments = virtualPath.Split('/').SelectMany(...)并获得类似于以下内容的结果:

/path
/path/default
/path/to
/path/to/default
/path/to/file
/path/to/file/default

virtualPath包含值"/path/to/file"

的位置

编辑:string.Format("{0}/{1}", ...)更改为VirtualPathUtility.Combine(..., ...)

有什么想法吗?

6 个答案:

答案 0 :(得分:3)

一种方法是逐步选择路径段,然后用空字符串和"/default"“连接”它以获得两个变体:

string path = @"/path/to/file";

string temp = "";

var query = path.Split('/')
                .Where(s => !string.IsNullOrEmpty(s))
                .Select((p) => {temp += ("/" + p); return temp;} )
                .SelectMany(s => new[]{"","/default"}.Select (d => s + d) );

答案 1 :(得分:2)

如果您首先定义这样的扩展方法:

public static IEnumerable<int> SplitIndexes(this string subject, char search)
{
    for(var i = 1; i < subject.Length; i++)
    {
        if(subject[i] == search)
        {
            yield return i;
        }
    }

    yield return subject.Length;
}

然后你可以这样做:

var endings = new string[] { string.Empty, "/default" };
var virtualPath = "/path/to/file";
var results = 
    from i in virtualPath.SplitIndexes('/')
    from e in endings
    select virtualPath.Substring(0, i) + e;

或者如果您更喜欢查询语法:

var endings = new string[] { string.Empty, "/default" };
var virtualPath = "/path/to/file";
var results = virtualPath.SplitIndexes('/')
                         .SelectMany(i => endings.Select(e => virtualPath.Substring(0, i) + e));

结果将是:

/path
/path/default
/path/to
/path/to/default
/path/to/file
/path/to/file/default

正如其他人所建议的那样,您可以使用Path.Combine以更加独立于平台的方式进行此操作,如下所示:

var endings = new string[] { string.Empty, "default" }; // Note: no / before default
var results = 
    from i in virtualPath.SplitIndexes(Path.DirectorySeparatorChar)
    from e in endings
    select Path.Combine(virtualPath.Substring(0, i), e);

答案 2 :(得分:2)

您正在寻找的高阶函数称为scan。普通LINQ中没有这样的功能,但您可以在MoreLinq中找到它。使用它,您的代码可能如下所示:

private List<string> CreateSegments(string virtualPath)
{
    return virtualPath.Split('/')
                      .Scan((s1, s2) => s1 + '/' + s2)
                      .Skip(1)
                      .SelectMany(p => new[] { p, p + "/default" })
                      .ToList();
}

这假设您的路径始终是以/开头的绝对路径。对于相对路径,您需要删除.Skip(1)部分。

如果您不想仅为此方法获取MoreLinq,则只需将its source复制到项目中即可。

答案 3 :(得分:2)

这可能会成功。它不是最简洁的代码,但它对我来说似乎非常易读。它使用字符串连接,因为对于短字符串,如路径或URL,它比任何替代方案都快。

编辑:修复并经过测试。

var query = path.Split(new[] {'/'}, StringSplitOptions.RemoveEmptyEntries)
    .Aggregate(new List<string>(), (memo, segment) => {

        memo.Add(memo.DefaultIfEmpty("").Last() + "/" + segment);

        return memo;

    }).Aggregate(new List<string>(), (memo, p) => {

        memo.Add(p);
        memo.Add(p + "/default");

        return memo;

    });

答案 4 :(得分:0)

到目前为止提供的答案更简洁,但这就是我想出的:

public static IEnumerable<string> PossiblePaths(string basePath)
{
    return PossiblePaths(basePath.Split(new[] { "/" }, 
                         StringSplitOptions.RemoveEmptyEntries));
}

private static IEnumerable<string> PossiblePaths(IEnumerable<string> segments, 
                                                 string current = "/")
{
    if (segments.Count() == 0)
    {
        return new string[0];
    }
    else
    {
        string next = current + segments.First();
        return new[] { next, next + "/default" }
                         .Concat(PossiblePaths(segments.Skip(1), next + "/"));
    }
}

答案 5 :(得分:0)

这样的事情:

public static IEnumerable<string> EnumerateSegments( this IEnumerable<string> segments )
{
  StringBuilder sb = new StringBuilder() ;
  foreach ( string segment in segements )
  {
    sb.Append( Path.DirectorySeparatorChar ).Append( segment ) ;
    yield return sb.ToString() ;
    int n = sb.Length ;
    sb.Append( Path.DirectorySeparatorChar ).Append("default") ;
    yield return sb.ToString() ;
    sb.Length = n ;
  }
}

应该做你。