【问题标题】:C# converting a VBA For Each loop to C# but gives compile errorC# 将 VBA For Each 循环转换为 C# 但给出编译错误
【发布时间】:2016-05-02 07:24:44
【问题描述】:

我正在将 Word VBA 宏转换为 C# 中的插件。

到目前为止,我已经成功地重构了 C# 中的所有语句、方法和属性,但是这让我很难过:

For Each l In Application.Languages
    If InStr(LCase(l.NameLocal), LCase(Language)) > 0 Then
        Selection.LanguageID = l.ID
        Exit For
    End If
Next l

我在 C# 中将上面的内容转换如下:

using Microsoft.Office;
using Microsoft.Office.Interop;
using Word = Microsoft.Office.Interop.Word;

Word.Application oWord = new Word.Application();
Word.Document oWordDoc = new Word.Document();
var Selection = oWordDoc.ActiveWindow.Selection;
string strTgtLanguage = "Hungarian";

foreach (var item in oWord.Application.Languages)
{
          if (item.NameLocal.IndexOf(strTgtLanguage)>-1) 
//The error is  ---^  here on 'NameLocal'.
          {
              Selection.LanguageID = item.ID
//And here on 'ID' -----------------------^
              break;
          }
}

两个实例的编译器错误是:

“object”不包含“NameLocal”的定义,并且找不到接受“object”类型的第一个参数的扩展方法“NameLocal”(您是否缺少 using 指令或程序集引用?)

我在这里做错了什么?我认为foreach 语句正确地声明了集合中的object

提前致谢。

【问题讨论】:

  • 在没有任何 Office 互操作库知识的情况下发言,我相信我在这里没有发现任何问题。您确定您的 C# 版本中的 Application.Languages 与您的 VB 版本中的相同吗?我看到它们是相同的名称,但它们是同一类型吗?这听起来像是出了什么问题。我在 C# 中看到了 oWord.Application.Languages,例如应该是 Application.Languages 吗?
  • 我发现的一个错误Selection.LanguageID = l.ID 应该是Selection.LanguageID = item.ID
  • 也就是说,我认为这不是严格意义上的语言问题。你有正确的语法,它的行为就像你期望的那样。这在 VB 中也是一个问题,所以我会以与您在那里相同的方式对其进行故障排除。但是,是的,Mosafizur 的评论也是正确的——只是第二条的一个错字。
  • 在不了解 Office Interop 的情况下也可以这么说,但 Application.Languages 很可能返回 IEnumerable,而不是 IEnumerable<SomeLanguageObject>。如果它应该返回一个特定类型的集合,你可以Cast它,即foreach (var item in oWord.Application.Languages.Cast<Language>()) { ... }
  • dee 可能是对的。根据文档,Languages 实现了非泛型IEnumerable,因此迭代器将返回Object 实例。而且,如果我没记错的话,VB 的 Object 是后期绑定的。将 var 更改为 Language 应该可以解决问题。

标签: c# vba foreach office-automation


【解决方案1】:

var item 替换为类型名称。

foreach (var item in oWord.Application.Languages) 替换为 foreach (Word.Language item in oWord.Languages)

var 的第一种情况下,变量item 的类型为System.Object

使用 Linq 的完整 for-each 可能如下所示:

foreach (Word.Language item in oWord.Languages.Cast<Word.Language>()
.Where(item => item.NameLocal.IndexOf(strTgtLanguage, StringComparison.Ordinal) > -1))
{
    selection.LanguageID = item.ID;
    break;
}

或者 for-each 可以替换为例如FirstOrDefault:

Word.Language hungarian =
oWord.Languages.Cast<Word.Language>()
.FirstOrDefault(item => item.NameLocal.IndexOf(strTgtLanguage, StringComparison.Ordinal) > -1);

if (hungarian != null)
    selection.LanguageID = hungarian.ID;

【讨论】:

  • 不是var item 是局部变量类型推断,以便编译器根据右侧的语句找出类型吗?换句话说:它不还是一个强类型代码吗?
  • @user3598756 是的,但是在foreach (var item in oWord.Application.Languages)的情况下,右边的类型是object,所以var会被object替换。语句foreach (Word.Language item in oWord.Application.Languages) 进行了强制转换。您可以在foreach (var item in oWord.Languages.Cast&lt;Word.Language&gt;()) 中使用var,因为演员已经在右侧完成了。
  • 感谢您的解释。我想我本可以在 VS 中验证这一点,但我不是(现在仍然不是!)它,而这 var 的东西总是让我模糊......
  • @dee 实际上,如果没有右侧的 IEnumerable 强制转换,如果我将 var 更改为强类型 Word.Language' it builds. But if I try to do the Cast`,它会导致编译器错误“Foreach 无法在 '方法组”。所以有效的是foreach (Word.Language item in oWord.Application.Languages)
  • 另一个问题是哪个被认为是更好的做法:foreachFirstOrDefault 与 LINQ?
【解决方案2】:

对于您的错误代码,在我看来,第一个错误是您的 oWord.Application.Languages 实际上并没有返回名为 NameLocal 的集合字段。

第二个Selection.LanguageID = l.ID应该是Selection.LanguageID = item.ID

【讨论】:

    【解决方案3】:

    Languages 是一个 IEnumerable,因此您需要显式声明迭代变量。

    如果您使用 IEnumerable 的 .Cast&lt;&gt;() 方法,您可以访问 .Any() 函数以避免自己编写迭代循环。

    下面给出了两个版本:

    using wd = Microsoft.Office.Interop.Word;
    
    wd.Application oWord = new wd.Application();
    wd.Document oWordDoc = new wd.Document();
    wd.Selection oWordSelection = oWordDoc.ActiveWindow.Selection;
    
    // foreach loop
    foreach (wd.Language item in oWord.Languages)
    {
        Console.WriteLine(item.ID);
        if (item.NameLocal.IndexOf("Hungarian") > -1)
        {
            oWordSelection.LanguageID = item.ID;
            break;
        }
    }
    
    // linq
    if (oWord.Languages.Cast<wd.Language>().Any(lang => lang.NameLocal.IndexOf("Hungarian") > -1))
        oWordSelection.LanguageID = wd.WdLanguageID.wdHungarian;
    

    【讨论】:

    • 这会在foreach 行上给出一个编译器错误,即wd 不是类型或命名空间。
    • 我使用了using wd = Microsoft.Office.Interop.Word。查看您的代码,我认为您使用了word,因此只需将wd 替换为word
    • 你能把这个using wd = Microsoft.Office.Interop.Word加到你代码的开头,这样就清楚了吗?
    • 我接受了你的,因为你是第一个,尽管@dee 写了同样的东西。
    • 欣赏这个想法,但我认为@dee 首先发布。无论你接受哪一个,我希望它能解决你的问题。
    猜你喜欢
    • 1970-01-01
    • 2012-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-03
    • 1970-01-01
    相关资源
    最近更新 更多