65.9K
CodeProject 正在变化。 阅读更多。
Home

Zip My Code

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.78/5 (17投票s)

2009年7月8日

CPOL

3分钟阅读

viewsIcon

75379

downloadIcon

2073

一个实用程序,用于精简你的源代码至核心要点,然后将其压缩成一个漂亮的 CodeProject 文章附件。

目录

引言

本文将为你介绍一个可能对 CodeProject 的贡献者很有用的实用程序。该实用程序可以轻松地将源代码剥离至文章代码附件应有的纯粹核心。通过简单的拖放或控制台界面来完成这一切也非常方便。如果足够多人觉得它有用,它可能会在“作者免费工具”页面上赢得一席之地,而我认为这个页面相当稀疏。

我知道这篇文章不会获得好评或人气,毕竟它只是一个非常基础的应用。它专注于解决一个小问题,而不是展示给你看非常酷的代码。但是,应用程序中埋藏着一些 金子般的精华,可能会让你感兴趣。

致谢

  • 首先,感谢 CodeProject 会员 SAKryukov 花时间审阅文章并提供极其建设性的反馈。
  • 感谢 MADEBITS 提供的 NETZ,我用它将 ZipMyCode 压缩和打包到一个程序集中。
  • 感谢 ICSharpCode 团队提供的 SharpZipLib,我用它进行 Zip 压缩。
  • 感谢 CodeProject 会员 Peter Palotas 提供的 Plossum 命令行解析器

如何使用该实用程序

用户界面部分

  1. 输入或按 浏览... 按钮并选择源路径。
  2. 开始压缩我的代码
  3. 一个压缩的 zip 存档将与你的源文件一起创建。

你可以通过展开 选项 来添加、编辑和删除排除模式。

控制台部分

除了用户界面,该应用程序还有一个命令行界面。

C:\>ZipMyCode /?
ZipMyCode  version 1.0.2.0
Copyright © 2009

Usage:
   ZipMyCode path [/c:file] [/e:value[+value]] [/o:file]
   ZipMyCode /u

Strip and compress:
   /c,                 Specifies the configuration file with exclude
   /configuration      patterns, one pattern per line. If file is
                       specified, the default exclude patterns are ignored.
   /e, /exclude        Specifies the list of exclude patterns. If patterns are
                       specified, the default exclude patterns are ignored.
   /o, /output         Specifies the name of the compressed output file.

Uninstallation:
   /u, /uninstall      Removes saved settings, i.e. cleans
                       up the application footprint.

Help:
   /?, /h, /help       Displays this help text.

该界面故意只接受一个路径作为参数,因为这样你就可以通过 Windows 的拖放功能将任何文件夹拖放到应用程序(或其快捷方式,如果你创建了一个)上,因为传递给应用程序的第一个参数就是被拖放的文件夹路径。

金子般的精华

使用 MVVM 和对话框服务构建

该应用程序是使用 MVVM 构建的,这似乎是目前大家都在讨论的主题。打开对话框是通过我上一篇文章中的对话框服务实现的:使用 MVVM 模式显示对话框。这一次,该服务包含一个 FolderBrowserDialog 而不是 OpenFileDialog。对于这些小型应用程序来说,由服务打开对话框的想法仍然可行,目前还没有出现问题。欢迎随意浏览一下这篇文章,并给我关于这个想法的意见。

编写属性的新方法

ViewModels 是用一种新的属性编写方式实现的。我们通常这样编写 ViewModel 属性:

private string someText;

public string SomeText
{
  get { return someText; }
  set
  {
    if (someText != value)
    {
      someText = value;
      OnPropertyChanged("SomeText");
    }
  }
}

借助一些方法的帮助,我们现在可以这样写:

public string SomeText
{
  get { return Property(() => SomeText); }
  set { Property(() => SomeText, value); }
}

我们不再需要编写重复的 setter 代码,而且 Visual Studio 的 IntelliSense 对这种格式的识别比包含字符串的第一个 OnPropertyChanged 方法更好。

支持这种新格式的代码存在于 ViewModelBase 中。

/// <summary>
/// Gets the value of a property matching the given expression.
/// </summary>
/// <typeparam name="T">The property type.</typeparam>
/// <param name="nameExpression">The expression pointing to the property.</param>
/// <returns>The property value if existing; otherwise default.</returns>
protected T Property<T>(Expression<Func<T>> nameExpression)
{
  PropertyItem p;
  if (properties.TryGetValue(nameExpression.ToString(), out p))
  {
    return (T)p.Value;
  }

  return default(T);
}

/// <summary>
/// Sets the value of a property matching the given expression.
/// </summary>
/// <typeparam name="T">The property type.</typeparam>
/// <param name="nameExpression">The expression pointing to the property.</param>
/// <param name="value">The value to set.</param>
protected void Property<T>(Expression<Func<T>> nameExpression, T value)
{
  // Get the key of the property
  string key = nameExpression.ToString();

  PropertyItem p;
  if (properties.TryGetValue(key, out p))
  {
    // Make sure the property value has changed
    if ((p.Value == null && value == null) || 
        (p.Value != null && p.Value.Equals(value)))
    {
      return;
    }

    // Set the new value
    p.Value = value;
  }
  else
  {
    // Create the new property item
    p = new PropertyItem
    {
      Name = GetPropertyName(nameExpression),
      Value = value
    };

    // Add the new propery item
    properties.Add(key, p);
  }

  // Raise property changed event
  OnPropertyChanged(new PropertyChangedEventArgs(p.Name));
}

/// <summary>
/// Gets the property name of the expression.
/// </summary>
/// <typeparam name="T">The property type.</typeparam>
/// <param name="nameExpression">The name expression.</param>
/// <returns>The property name of the expression.</returns>
private static string GetPropertyName<T>(Expression<Func<T>> nameExpression)
{
  UnaryExpression unaryExpression = nameExpression.Body as UnaryExpression;

  // Convert name expression into MemberExpression
  MemberExpression memberExpression = unaryExpression != null ?
    (MemberExpression)unaryExpression.Operand :
    (MemberExpression)nameExpression.Body;

  return memberExpression.Member.Name;
}

/// <summary>
/// Class wrapping up the essential parts of a property.
/// </summary>
class PropertyItem
{
  /// <summary>
  /// Gets or sets the name.
  /// </summary>
  public string Name { get; set; }

  /// <summary>
  /// Gets or sets the value.
  /// </summary>

  public object Value { get; set; }
}

使用排除模式搜索文件和文件夹的算法

.NET 框架支持使用 DirectoryInfo.GetFiles(string searchPattern)DirectoryInfo.GetDirectories(string searchPattern) 来搜索文件和文件夹,但它不提供使用排除模式搜索的功能。当决定哪些文件和文件夹可以包含在压缩的源代码附件中时,我决定让用户声明他们不想要什么比声明他们想要什么更容易。

以下代码存在于 GetFilesTask 中,并使用 LINQ 来解决这个问题:

/// <summary>
/// Recursive method finding all files from a directory and its sub-directory.
/// </summary>
/// <param name="directory">The directory.</param>
/// <param name="excludePatterns">The file and directory exclude pattern.</param>
private IEnumerable<string> RecursiveGetFiles(DirectoryInfo directory,
  string[] excludePatterns)
{
  // Find files not matching the exclude patterns
  IEnumerable<FileInfo> files =
    (from file in directory.GetFiles()
     select file)
    .Except(GetExcludedFiles(directory, excludePatterns), new FileInfoEqualityComparer());

  // Yield files not matching the exclude patterns
  foreach (FileInfo file in files)
  {
    yield return file.FullName;
  }

  // Find directories not matching the exclude patterns
  IEnumerable<DirectoryInfo> subDirectories =
    (from subDirectory in directory.GetDirectories()
     select subDirectory)
    .Except(GetExcludedDirectories(directory, excludePatterns),
      new DirectoryInfoEqualityComparer());

  // Search files in sub-directories not matching the exclude pattern
  foreach (DirectoryInfo subDirectory in subDirectories)
  {
    // Yield all files not matching the exclude patterns
    foreach (string file in RecursiveGetFiles(subDirectory, excludePatterns))
    {
      yield return file;
    }
  }
}

/// <summary>
/// Gets the excluded files in a specified directory.
/// </summary>
/// <param name="directory">The directory.</param>
/// <param name="excludePatterns">The file exclude pattern.</param>
private IEnumerable<FileInfo> GetExcludedFiles(DirectoryInfo directory,
  string[] excludePatterns)
{
  return
    from excludePattern in excludePatterns
    from filesMatchingExcludePattern in directory.GetFiles(excludePattern)
    select filesMatchingExcludePattern;
}

/// <summary>

/// Gets the excluded sub-directories in a specified directory.
/// </summary>
/// <param name="directory">The directory.</param>
/// <param name="excludePatterns">The directory exclude pattern.</param>
private IEnumerable<DirectoryInfo> GetExcludedDirectories(DirectoryInfo directory,
  string[] excludePatterns)
{
  return
    from excludePattern in excludePatterns
    from directoriesMatchingExcludePattern in directory.GetDirectories(excludePattern)
    select directoriesMatchingExcludePattern;
}

class FileInfoEqualityComparer : IEqualityComparer<FileInfo>

{
  public bool Equals(FileInfo x, FileInfo y)
  {
    return x.FullName.Equals(y.FullName);
  }

  public int GetHashCode(FileInfo obj)
  {
    return obj.FullName.GetHashCode();
  }
}

class DirectoryInfoEqualityComparer : IEqualityComparer<DirectoryInfo>
{
  public bool Equals(DirectoryInfo x, DirectoryInfo y)
  {
    return x.FullName.Equals(y.FullName);
  }

  public int GetHashCode(DirectoryInfo obj)
  {
    return obj.FullName.GetHashCode();
  }
}

历史

  • 1.0.2.0 (2009年12月20日)
    • 控制台
      • 实现了控制台界面。
      • 实现了卸载。
    • UI
      • 源路径可编辑并进行验证。
      • 更改了默认路径。
      • 修复了初始焦点。
      • 添加了额外的默认忽略模式。
  • 1.0.1.0 (2009年10月25日)
  • 1.0.0.0 (2009年7月7日)
    • 初始版本
© . All rights reserved.