- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
初来乍到了解一门新的语言,它可能和熟悉的c/c++有不小差别,整体上需要首先了解下语法文件的整体结构。例如,源文件整体结构如何.
乍看CSharp源文件(compile unit)的结构, 官网 主要是通过文字描述的整体结构,而下面的形式化 语法 ,描述也不太符合自定向下这种类型的语法结构描述方法,这样对于新手来了解这种语言的整体结构来说就有些困难.
好在有一个开源的 dotgnu 项目,该项目的 官方文档 中显示,项目已经在2012年正式废弃(可能更早已经没有更新了)。从工程的语法描述文件来看,它还没有涉及到lambda表达式这种重要语法功能的支持,不知道是因为项目启动时暂时没有支持,或者是启动时CSharp还没有这种语法功能.
As of December 2012, the DotGNU project has been decommissioned, until and unless a substantial new volunteer effort arises. The exception is the libjit component, which is now a separate libjit package. 。
尽管该项目比较久远,但是它的语法描述是通过经典的yacc语法描述,这样对于理解整体结构时最为直观的。其中对于整体结构的描述大致如下。从这个描述来看,整个源文件的结构顶层只能包含using、namespace、class、enum、struct、module、interface、delegate这些声明.
///@file: DotGnu\pnet\cscc\csharp\cs_grammar.y
/*
* Outer level of the C# input file.
*/
CompilationUnit
: /* empty */ {
/* The input file is empty */
CCTypedWarning("-empty-input",
"file contains no declarations");
ResetState();
}
| OuterDeclarationsRecoverable {
/* Check for empty input and finalize the parse */
if(!HaveDecls)
{
CCTypedWarning("-empty-input",
"file contains no declarations");
}
ResetState();
}
| OuterDeclarationsRecoverable NonOptAttributes {
/* A file that contains declarations and assembly attributes */
if($2)
{
InitGlobalNamespace();
CCPluginAddStandaloneAttrs
(ILNode_StandaloneAttr_create
((ILNode*)CurrNamespaceNode, $2));
}
ResetState();
}
| NonOptAttributes {
/* A file that contains only assembly attributes */
if($1)
{
InitGlobalNamespace();
CCPluginAddStandaloneAttrs
(ILNode_StandaloneAttr_create
((ILNode*)CurrNamespaceNode, $1));
}
ResetState();
}
;
/*
* Note: strictly speaking, declarations should be ordered so
* that using declarations always come before namespace members.
* We have relaxed this to make error recovery easier.
*/
OuterDeclarations
: OuterDeclaration
| OuterDeclarations OuterDeclaration
;
OuterDeclaration
: UsingDirective
| NamespaceMemberDeclaration
| error {
/*
* This production recovers from errors at the outer level
* by skipping invalid tokens until a namespace, using,
* type declaration, or attribute, is encountered.
*/
#ifdef YYEOF
while(yychar != YYEOF)
#else
while(yychar >= 0)
#endif
{
if(yychar == NAMESPACE || yychar == USING ||
yychar == PUBLIC || yychar == INTERNAL ||
yychar == UNSAFE || yychar == SEALED ||
yychar == ABSTRACT || yychar == CLASS ||
yychar == STRUCT || yychar == DELEGATE ||
yychar == ENUM || yychar == INTERFACE ||
yychar == '[')
{
/* This token starts a new outer-level declaration */
break;
}
else if(yychar == '}' && CurrNamespace.len != 0)
{
/* Probably the end of the enclosing namespace */
break;
}
else if(yychar == ';')
{
/* Probably the end of an outer-level declaration,
so restart the parser on the next token */
yychar = YYLEX;
break;
}
yychar = YYLEX;
}
#ifdef YYEOF
if(yychar != YYEOF)
#else
if(yychar >= 0)
#endif
{
yyerrok;
}
NestingLevel = 0;
}
;
///....
OptNamespaceMemberDeclarations
: /* empty */
| OuterDeclarations
;
NamespaceMemberDeclaration
: NamespaceDeclaration
| TypeDeclaration { CCPluginAddTopLevel($1); }
;
TypeDeclaration
: ClassDeclaration { $$ = $1; }
| ModuleDeclaration { $$ = $1; }
| StructDeclaration { $$ = $1; }
| InterfaceDeclaration { $$ = $1; }
| EnumDeclaration { $$ = $1; }
| DelegateDeclaration { $$ = $1; }
;
微软官方开源了 CSharp的实现 ,所以最标准的解释应该是来自微软官方代码。遗憾的是这个工程是使用CSharp开发的,所以项目内对于语法的解析也不是通过yacc文件描述,而是手工实现的一个编译器解析。猜测代码应该位于 。
///@file: roslyn\src\Compilers\CSharp\Portable\Parser
internal CompilationUnitSyntax ParseCompilationUnitCore()
{
SyntaxToken? tmp = null;
SyntaxListBuilder? initialBadNodes = null;
var body = new NamespaceBodyBuilder(_pool);
try
{
this.ParseNamespaceBody(ref tmp, ref body, ref initialBadNodes, SyntaxKind.CompilationUnit);
var eof = this.EatToken(SyntaxKind.EndOfFileToken);
var result = _syntaxFactory.CompilationUnit(body.Externs, body.Usings, body.Attributes, body.Members, eof);
if (initialBadNodes != null)
{
// attach initial bad nodes as leading trivia on first token
result = AddLeadingSkippedSyntax(result, initialBadNodes.ToListNode());
_pool.Free(initialBadNodes);
}
return result;
}
finally
{
body.Free(_pool);
}
}
private void ParseNamespaceBody(
[NotNullIfNotNull(nameof(openBraceOrSemicolon))] ref SyntaxToken? openBraceOrSemicolon,
ref NamespaceBodyBuilder body,
ref SyntaxListBuilder? initialBadNodes,
SyntaxKind parentKind)
{
// "top-level" expressions and statements should never occur inside an asynchronous context
Debug.Assert(!IsInAsync);
bool isGlobal = openBraceOrSemicolon == null;
var saveTerm = _termState;
_termState |= TerminatorState.IsNamespaceMemberStartOrStop;
NamespaceParts seen = NamespaceParts.None;
var pendingIncompleteMembers = _pool.Allocate<MemberDeclarationSyntax>();
bool reportUnexpectedToken = true;
try
{
while (true)
{
switch (this.CurrentToken.Kind)
{
case SyntaxKind.NamespaceKeyword:
// incomplete members must be processed before we add any nodes to the body:
AddIncompleteMembers(ref pendingIncompleteMembers, ref body);
var attributeLists = _pool.Allocate<AttributeListSyntax>();
var modifiers = _pool.Allocate();
body.Members.Add(adjustStateAndReportStatementOutOfOrder(ref seen, this.ParseNamespaceDeclaration(attributeLists, modifiers)));
_pool.Free(attributeLists);
_pool.Free(modifiers);
reportUnexpectedToken = true;
break;
case SyntaxKind.CloseBraceToken:
// A very common user error is to type an additional }
// somewhere in the file. This will cause us to stop parsing
// the root (global) namespace too early and will make the
// rest of the file unparseable and unusable by intellisense.
// We detect that case here and we skip the close curly and
// continue parsing as if we did not see the }
if (isGlobal)
{
// incomplete members must be processed before we add any nodes to the body:
ReduceIncompleteMembers(ref pendingIncompleteMembers, ref openBraceOrSemicolon, ref body, ref initialBadNodes);
var token = this.EatToken();
token = this.AddError(token,
IsScript ? ErrorCode.ERR_GlobalDefinitionOrStatementExpected : ErrorCode.ERR_EOFExpected);
this.AddSkippedNamespaceText(ref openBraceOrSemicolon, ref body, ref initialBadNodes, token);
reportUnexpectedToken = true;
break;
}
else
{
// This token marks the end of a namespace body
return;
}
case SyntaxKind.EndOfFileToken:
// This token marks the end of a namespace body
return;
case SyntaxKind.ExternKeyword:
if (isGlobal && !ScanExternAliasDirective())
{
// extern member or a local function
goto default;
}
else
{
// incomplete members must be processed before we add any nodes to the body:
ReduceIncompleteMembers(ref pendingIncompleteMembers, ref openBraceOrSemicolon, ref body, ref initialBadNodes);
var @extern = ParseExternAliasDirective();
if (seen > NamespaceParts.ExternAliases)
{
@extern = this.AddErrorToFirstToken(@extern, ErrorCode.ERR_ExternAfterElements);
this.AddSkippedNamespaceText(ref openBraceOrSemicolon, ref body, ref initialBadNodes, @extern);
}
else
{
body.Externs.Add(@extern);
seen = NamespaceParts.ExternAliases;
}
reportUnexpectedToken = true;
break;
}
case SyntaxKind.UsingKeyword:
if (isGlobal && (this.PeekToken(1).Kind == SyntaxKind.OpenParenToken || (!IsScript && IsPossibleTopLevelUsingLocalDeclarationStatement())))
{
// Top-level using statement or using local declaration
goto default;
}
else
{
parseUsingDirective(ref openBraceOrSemicolon, ref body, ref initialBadNodes, ref seen, ref pendingIncompleteMembers);
}
reportUnexpectedToken = true;
break;
case SyntaxKind.IdentifierToken:
if (this.CurrentToken.ContextualKind != SyntaxKind.GlobalKeyword || this.PeekToken(1).Kind != SyntaxKind.UsingKeyword)
{
goto default;
}
else
{
parseUsingDirective(ref openBraceOrSemicolon, ref body, ref initialBadNodes, ref seen, ref pendingIncompleteMembers);
}
reportUnexpectedToken = true;
break;
case SyntaxKind.OpenBracketToken:
if (this.IsPossibleGlobalAttributeDeclaration())
{
// incomplete members must be processed before we add any nodes to the body:
ReduceIncompleteMembers(ref pendingIncompleteMembers, ref openBraceOrSemicolon, ref body, ref initialBadNodes);
var attribute = this.ParseAttributeDeclaration();
if (!isGlobal || seen > NamespaceParts.GlobalAttributes)
{
RoslynDebug.Assert(attribute.Target != null, "Must have a target as IsPossibleGlobalAttributeDeclaration checks for that");
attribute = this.AddError(attribute, attribute.Target.Identifier, ErrorCode.ERR_GlobalAttributesNotFirst);
this.AddSkippedNamespaceText(ref openBraceOrSemicolon, ref body, ref initialBadNodes, attribute);
}
else
{
body.Attributes.Add(attribute);
seen = NamespaceParts.GlobalAttributes;
}
reportUnexpectedToken = true;
break;
}
goto default;
default:
var memberOrStatement = isGlobal ? this.ParseMemberDeclarationOrStatement(parentKind) : this.ParseMemberDeclaration(parentKind);
if (memberOrStatement == null)
{
// incomplete members must be processed before we add any nodes to the body:
ReduceIncompleteMembers(ref pendingIncompleteMembers, ref openBraceOrSemicolon, ref body, ref initialBadNodes);
// eat one token and try to parse declaration or statement again:
var skippedToken = EatToken();
if (reportUnexpectedToken && !skippedToken.ContainsDiagnostics)
{
skippedToken = this.AddError(skippedToken,
IsScript ? ErrorCode.ERR_GlobalDefinitionOrStatementExpected : ErrorCode.ERR_EOFExpected);
// do not report the error multiple times for subsequent tokens:
reportUnexpectedToken = false;
}
this.AddSkippedNamespaceText(ref openBraceOrSemicolon, ref body, ref initialBadNodes, skippedToken);
}
else if (memberOrStatement.Kind == SyntaxKind.IncompleteMember && seen < NamespaceParts.MembersAndStatements)
{
pendingIncompleteMembers.Add(memberOrStatement);
reportUnexpectedToken = true;
}
else
{
// incomplete members must be processed before we add any nodes to the body:
AddIncompleteMembers(ref pendingIncompleteMembers, ref body);
body.Members.Add(adjustStateAndReportStatementOutOfOrder(ref seen, memberOrStatement));
reportUnexpectedToken = true;
}
break;
}
}
}
finally
{
_termState = saveTerm;
// adds pending incomplete nodes:
AddIncompleteMembers(ref pendingIncompleteMembers, ref body);
_pool.Free(pendingIncompleteMembers);
}
MemberDeclarationSyntax adjustStateAndReportStatementOutOfOrder(ref NamespaceParts seen, MemberDeclarationSyntax memberOrStatement)
{
switch (memberOrStatement.Kind)
{
case SyntaxKind.GlobalStatement:
if (seen < NamespaceParts.MembersAndStatements)
{
seen = NamespaceParts.MembersAndStatements;
}
else if (seen == NamespaceParts.TypesAndNamespaces)
{
seen = NamespaceParts.TopLevelStatementsAfterTypesAndNamespaces;
if (!IsScript)
{
memberOrStatement = this.AddError(memberOrStatement, ErrorCode.ERR_TopLevelStatementAfterNamespaceOrType);
}
}
break;
case SyntaxKind.NamespaceDeclaration:
case SyntaxKind.FileScopedNamespaceDeclaration:
case SyntaxKind.EnumDeclaration:
case SyntaxKind.StructDeclaration:
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.DelegateDeclaration:
case SyntaxKind.RecordDeclaration:
case SyntaxKind.RecordStructDeclaration:
if (seen < NamespaceParts.TypesAndNamespaces)
{
seen = NamespaceParts.TypesAndNamespaces;
}
break;
default:
if (seen < NamespaceParts.MembersAndStatements)
{
seen = NamespaceParts.MembersAndStatements;
}
break;
}
return memberOrStatement;
}
void parseUsingDirective(
ref SyntaxToken? openBrace,
ref NamespaceBodyBuilder body,
ref SyntaxListBuilder? initialBadNodes,
ref NamespaceParts seen,
ref SyntaxListBuilder<MemberDeclarationSyntax> pendingIncompleteMembers)
{
// incomplete members must be processed before we add any nodes to the body:
ReduceIncompleteMembers(ref pendingIncompleteMembers, ref openBrace, ref body, ref initialBadNodes);
var @using = this.ParseUsingDirective();
if (seen > NamespaceParts.Usings)
{
@using = this.AddError(@using, ErrorCode.ERR_UsingAfterElements);
this.AddSkippedNamespaceText(ref openBrace, ref body, ref initialBadNodes, @using);
}
else
{
body.Usings.Add(@using);
seen = NamespaceParts.Usings;
}
}
}
因为这个这种手撕的编译器代码看起来过于晦涩,又回头看了下CSharp的 官方语言描述 ,其中是有编译单元入口描述的,只是隐藏的位置比较深,所以刚开始没看到([流汗]),这个最顶层的语法结构就是compilation_unit,从这个依次向下可以看到对于该结构的逐层描述和细化。从这个语法描述结构来看,最顶层的结构的确只能宝库using开始的结构,然后就是namespace,以及type_declaration.
// Source: §14.2 Compilation units
compilation_unit
: extern_alias_directive* using_directive* global_attributes?
namespace_member_declaration*
;
// Source: §22.3 Attribute specification
global_attributes
: global_attribute_section+
;
// Source: §14.6 Namespace member declarations
namespace_member_declaration
: namespace_declaration
| type_declaration
;
// Source: §14.7 Type declarations
type_declaration
: class_declaration
| struct_declaration
| interface_declaration
| enum_declaration
| delegate_declaration
;
// Source: §14.3 Namespace declarations
namespace_declaration
: 'namespace' qualified_identifier namespace_body ';'?
;
global_attribute_section
: '[' global_attribute_target_specifier attribute_list ']'
| '[' global_attribute_target_specifier attribute_list ',' ']'
;
在众多表达式中,这种lambda是一种比较顺手的语法结构,经在很多项目中出镜率还是很高的,所以还是要看下这个语法。在这个语法描述中,可以看到,关键的是"=>"这个语法结构,在这个结构之前,可以使用括弧(explicit_anonymous_function_signature),也可以不使用(implicit_anonymous_function_signature)。这种语法其实很难使用yacc语法描述,因为它对上下文的依赖非常强.
// Source: §12.19.1 General
lambda_expression
: 'async'? anonymous_function_signature '=>' anonymous_function_body
;
anonymous_function_signature
: explicit_anonymous_function_signature
| implicit_anonymous_function_signature
;
explicit_anonymous_function_signature
: '(' explicit_anonymous_function_parameter_list? ')'
;
implicit_anonymous_function_signature
: '(' implicit_anonymous_function_parameter_list? ')'
| implicit_anonymous_function_parameter
;
implicit_anonymous_function_parameter_list
: implicit_anonymous_function_parameter
(',' implicit_anonymous_function_parameter)*
;
implicit_anonymous_function_parameter
: identifier
;
搜索语法中的这个'=>',可以发现除了lambda表达式之外,还有其他的场景使用,例如local_function_body。同样是这种语法结构,那么如何区域分是lambda表达式还是local_function呢?其实看下语法的上下文就可以看到,localfunction中'=>'前面是需要有类型(return_type)声明,而lambda表达式中的implicit_anonymous_function_parameter是作为expression来出现的,而顾名思义,expression表达式的前面是不可能出现type这种类型前缀引导的.
这里再次看到,CSharp这种语言是很难通过yacc这种通用的语法工具来描述.
// Source: §13.6.4 Local function declarations
local_function_declaration
: local_function_header local_function_body
;
local_function_header
: local_function_modifier* return_type identifier type_parameter_list?
( formal_parameter_list? ) type_parameter_constraints_clause*
;
local_function_modifier
: 'async'
| 'unsafe'
;
local_function_body
: block
| '=>' null_conditional_invocation_expression ';'
| '=>' expression ';'
;
一个直接的推论是: 不存在类似于C/C++中“全局变量”的概念 .
由于不存在全局变量或者函数,所以也不存在类似于C/C++的全局main函数入口,所以整个应用(application)的入口只能位于某个class(不特定)内部,语言规定作为必须声明为static public类型.
从语法上看,namespace并不是必须的,如果没有把声明放在namespace中,那么和C++一样,声明会放在 全局globalnamespace 中.
但是,按照语法规范写的代码并不代表就是合法的。例如下面根据语法规范写的代码,大部分都是错误:-(——编程好难啊…… 。
using System;
//命名空间不能直接包含字段或方法之类的成员
int leela = 1;
namespace harry
{
class harry
{
public static int fry(int x, int y)
{
int localfunc() => x + y;
//只有 assignment、call、increment、decrement 和 new 对象表达式可用作语句
z => z + 1;
//error CS0149: 应输入方法名称
int dd = ((int a) => a + 1)(1);
return localfunc();
}
public static int Main()
{
return fry(3, 7);
}
};
}
namespace tsecer
{
//命名空间不能直接包含字段或方法之类的成员
void tsecer(){}
}
最后此篇关于CSharp初体验的文章就讲到这里了,如果你想了解更多关于CSharp初体验的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我希望将我们代码中某些基于反射的部分替换为使用动态运行时编译性能更好的部分。环顾四周,我看到 Mono 和 Microsoft 都有单独的编译器即服务解决方案:Mono.CSharp 和 Micros
在 Open with .cs 的菜单文件有 Csharp editor和 Csharp editor with encoding .我打开了一个解决方案,并没有看到任何区别。 它们之间有什么区别?
关闭。此题需要details or clarity 。目前不接受答案。 想要改进这个问题吗?通过 editing this post 添加详细信息并澄清问题. 已关闭 7 年前。 Improve th
一段代码抵千字... public enum enTest { a, b, c } public void PrintEnum() { foreach (var E in Enum.GetVa
我不熟悉 visual studio/c# 中的单元测试和异步操作。感谢对此的任何帮助。 我的主类 class Foo { public async Task GetWebAsync()
我搜索了一上午,似乎找不到这个问题的答案。 我有一组线程,每个线程都在工作,然后我将遍历 id 连接每个线程,然后启动新线程。检测线程何时完成以便我可以在不等待每个线程完成的情况下启动新线程的最佳方法
我有方法,它有参数“word”返回 Word 第一个字母和字符串的最后一个字母。第一个和最后一个字母之间有三点。例如,当您编写“stackoverflow”时,它会像“s...w”那样返回它 我有这个
这是我目前正在尝试的: var value = Datepicker1.Value; List data = new List(); string connetionString; MySqlConn
A a = new A(); 012E2FB8 mov ecx,58812BCh 012E2FBD调用011C30F4 012E2FC2 mov dword ptr [ebp-44h],eax 012
我有这样的 file.txt:编辑:我没有写,但我猜这很重要 - 在 file.txt 中可以有其他行! folder=c:\user;c:\test;c:\something; 我需要添加一个路径,
我从 OdcDataReader 获取一个值,该值显示为 Single 类型。我不想在代码中使用单个,所以我想将它转换成小数。但是,似乎每当我尝试将其转换为小数(或任何与此相关的东西)时,它都会丢失几
你能在最顶层的窗口上启动一个进程吗? (csharp wpf) 我有以下内容,但在此之前的当前窗口(使用具有 topmost=true 的窗口类的 wpf 窗口)在进程启动时仍位于进程的顶部。 if
我正在尝试在可移植类库中使用 .NET 4 的动态类型,但我在获取所有必需的引用时遇到了问题。我可以很好地引用 System.Core,但是 Microsoft.CSharp 给我带来了一些麻烦。似乎
我相信我发现了一个错误,该错误涉及如何将准备好的语句缓存在 Cassandra csharp 驱动程序(版本 2.7.3)的 StatementFactory 中。这是用例。 Guid key = G
大家好,我只想将 textbox.text 与数据库进行比较。这是我的代码: string str = string.Format("select Firstname,Lastname,Middlen
是否有一个简单的 csharp (cshtml) 解决方案来检测浏览器是否为 Chrome,然后不加载一段 html 代码。另外,我知道我可能只使用 javascript,但使用 C# html 组合
我有一个 Minecraft 客户端启动器,但它无法启动 Minecraft。我尝试了 TeamExtreme 中的参数,但失败了。 [原文为匈牙利,现翻译] 启动器代码: priva
是否可以在我的 CSharp 项目文件中包含代码来检查 ConfuserEx 是否正确执行,如果没有正确执行则终止编译过程?也许使用反射来检查类名是否仍然存在(即没有被混淆)? 更新:这是 .cspr
我有一堆实现接口(interface)的数据类,我们称它为ISpendable。根据在运行时处理哪个 ISpendable 类, Controller 类需要设置不同的事件监听器。 例子: publi
我已阅读构建说明 protobuf-csharp-port在Linux/Unix 系统下使用Mono。我仍然无法让它工作,最终我会打开一个线程来询问为什么,但这不是这篇文章的问题。 我正在使用 Win
我是一名优秀的程序员,十分优秀!