我清楚地理解在处理语句时使用C#编译器的"基于模式"的方法foreach.
从C#语言规范(第8.8.4节)可以看出,首先C#编译器尝试查找GetEnumerator方法,然后才尝试查找IEnumerable<T>和IEnumerable接口.
但我不清楚,为什么C#编译器string单独处理(因为String该类包含一个GetEnumerator返回的方法,CharEnumerator它也实现IEnumerable<char>和IEnumerable交互):
string s = "1234";
foreach(char c in s)
Console.WriteLine(c);
Run Code Online (Sandbox Code Playgroud)
转换为
string s = "1234";
for(int i = 0; i < s.Length; i++)
Console.WriteLine(s[i]);
Run Code Online (Sandbox Code Playgroud)
但我在语言规范中找不到关于String该类的任何例外.有人可以提供一些有关此解决方案的见解吗?
我尝试使用C#4编译器.以下是以前代码段的IL代码:
IL_0000: ldstr "1234"
IL_0005: stloc.0
IL_0006: ldloc.0
IL_0007: stloc.2
IL_0008: ldc.i4.0
IL_0009: stloc.3
IL_000A: br.s IL_001E
IL_000C: ldloc.2
IL_000D: ldloc.3
IL_000E: callvirt System.String.get_Chars
IL_0013: stloc.1
IL_0014: ldloc.1
IL_0015: call …Run Code Online (Sandbox Code Playgroud) F#具有称为"类型扩展"的功能,使开发人员能够扩展现有类型.有两种类型的扩展:内在扩展和可选扩展.第一个类似于C#中的部分类型,第二个类似于方法扩展(但更强大).
要使用内部扩展,我们应该将两个声明放入同一个文件中.在这种情况下,编译器会将两个定义合并为一个最终类型(即这是一种类型的两个"部分").
问题是这两种类型对不同的成员和值有不同的访问规则:
// SampleType.fs
// "Main" declaration
type SampleType(a: int) =
let f1 = 42
let func() = 42
[<DefaultValue>]
val mutable f2: int
member private x.f3 = 42
static member private f4 = 42
member private this.someMethod() =
// "Main" declaration has access to all values (a, f1 and func())
// as well as to all members (f2, f3, f4)
printf "a: %d, f1: %d, f2: %d, f3: %d, f4: %d, func(): …Run Code Online (Sandbox Code Playgroud) 我没有,但我不说没有.
所有阅读此内容的C#开发人员可能都知道内部受保护的内容以及何时使用它.我的问题很简单:您是否真的使用过它,或者使用受保护的内部访问修饰符处理成功设计的项目?如果是的话,请分享您的知识并发布好样本,我终于可以欣赏这个棘手的修饰符的巧妙使用.
//我相信这不是主观的,我实际上是在寻求答案;-)
C#和F#具有默认(或可选)参数的不同实现.
在C#语言中,当您为参数添加默认值时,您不会更改其基础类型(我的意思是参数的类型).实际上C#中的可选参数是一个轻量级的语法糖:
class CSharpOptionalArgs
{
public static void Foo(int n = 0) {}
}
// Somewhere in the call site
CSharpOptionalArgs.Foo();
// Call to Foo() will be transformed by C# compiler
// *at compile time* to something like:
const int nArg = GetFoosDefaultArgFromTheMetadata();
CSharpOptionalArgs.Foo(nArg);
Run Code Online (Sandbox Code Playgroud)
但是F#以不同的方式实现了这个功能.与C#不同,F#可选参数在被调用者站点解析,但不在调用者站点解析:
type FSharpOptionalArgs() =
static let defaultValue() = 42
static member public Foo(?xArg) =
// Callee site decides what value to use if caller does not specifies it
let x = defaultArg …Run Code Online (Sandbox Code Playgroud) 考虑以下课程:
class C1 : IDisposable {...}
class C2 : IDisposable {...}
sealed class C3 : IDisposable
{
public C3()
{
c1 = new C1();
throw new Exception(); //oops!
}
~C3()
{
//What can we do???
}
public void Dispose()
{
if ( c1 != null ) c1.Dispose();
if ( c2 != null ) c2.Dispose();
}
private C1 c1;
private C2 c2;
//assume that this class does not contains native resources
}
Run Code Online (Sandbox Code Playgroud)
现在,假设我们正确地使用一次性物体:
using (var c3 = new C3())
{
} …Run Code Online (Sandbox Code Playgroud) There is a few cases when F# records behavior is strange to me:
No warning on ambiguity
type AnotherPerson = {Id: int; Name: string}
type Person = {Id: int; Name: string;}
// F# compiler will use second type without any complains or warnings
let p = {Id = 42; Name = "Foo";}
Run Code Online (Sandbox Code Playgroud)
Warning on records deconstruction instead of records construction
Instead of getting a warning on records construction in previous case, F# compiler issued a warning on records "deconstruction":
// …Run Code Online (Sandbox Code Playgroud) 我是一位C#开发人员,他正在尝试使用PowerShell构建有用的东西。这就是为什么我一直试图在PowerShell中使用来自.NET世界的著名习语的原因。
我正在编写一个具有不同抽象层的脚本:数据库操作,文件操作等。在某些时候,我想捕获一个错误并将其包装为对最终用户更有意义的东西。这是C#/ Java / C ++代码的常见模式:
Function LowLevelFunction($arg)
{
# Doing something very useful here!
# but this operation could throw
if (!$arg) {throw "Ooops! Can't do this!"}
}
Run Code Online (Sandbox Code Playgroud)
现在,我想调用此函数并包装一个错误:
Function HighLevelFunction
{
Try
{
LowLevelFunction
}
Catch
{
throw "HighLevelFunction failed with an error!`nPlease check inner exception for more details!`n$_"
}
}
Run Code Online (Sandbox Code Playgroud)
这种方法几乎是我所需要的,因为HighLevelFunction它将引发新的错误,而原始错误的根本原因将会丢失!
在C#代码中,我总是可以抛出新的异常并将原始异常作为内部异常提供。在这种情况下,HighLevelFunction将能够以对他们的客户更有意义的形式传达他们的错误,但仍将提供内部细节以用于诊断目的。
在PowerShell中打印原始异常的唯一方法是使用$Error存储所有异常的变量。可以,但是我脚本的用户(现在是我自己)应该做更多我想做的事情。
所以问题是:有什么方法可以在PowerShell中引发异常并将原始错误提供为内部错误?
我想扩展一个现有的"核心"模块,例如Core.Option:
module Microsoft.FSharp.Core.Option
let filter predicate op =
match op with
| Some(v) -> if predicate(v) then Some(v) else None
| None -> None
Run Code Online (Sandbox Code Playgroud)
(我知道bind功能,但我认为filter在某些情况下选项的方法更方便).
但不幸的是,我无法在filter没有明确打开Microsoft.FSharp.Core命名空间的情况下使用方法:
// Commenting following line will break the code!
open Microsoft.FSharp.Core
let v1 = Some 42
let v2 = v1 |> Option.filter (fun v -> v > 40)
printfn "v2 is: %A" v2
Run Code Online (Sandbox Code Playgroud)
在大多数情况下,我们不能在不打开适当的命名空间的情 F#编译器自动"打开"一些预定义(核心)命名空间(如Microsoft.FSharp.Core),这不会从"模块扩展"引入范围方法,我们仍然应该手动打开核心命名空间.
我的问题是:有没有解决方法?
或者扩展"核心"模块的最佳方法是在自定义命名空间中创建此类扩展并手动打开此命名空间?
// Lets custom Option module in …Run Code Online (Sandbox Code Playgroud) 目前我在我的脚本中使用以下结构:
HighLevel.ps1
LowLevelModule1.psm1
LowLevelModule2.psm1
...
Run Code Online (Sandbox Code Playgroud)
现在,我在ps1文件中设置严格模式,以便在运行时至少获得一些类型安全性.不幸的是,cllaing Set-StrictMode -Version Latest只能为当前作用域和所有子作用域启用严格模式(这是设计使然.证明:https://technet.microsoft.com/en-us/library/hh849692.aspx).
据我了解当前的PS架构,在ps1文件中更改严格模式会更改脚本级别配置.但模块有自己的脚本范围,因此模块不会继承父级规则.
作为一种解决方法,我可以放到Set-StrictMode每个PSM1文件,但它似乎不是最好的方法,因为我无法给我的模块的客户端决定,打开严格模式或不.
$VerbosePreferences配置存在同样的问题.每个范围也启用此配置,因此我必须跨模块边界传播此信息.
有关如何全局更改严格模式和详细偏好的任何建议吗?
PS为此目的更改$ profile不是一个选项.
另一个与F#功能相关的问题称为"类型扩展".
在F#中扩展枚举似乎是不可能的.我使用C#Extensions方法来扩展枚举:添加范围验证逻辑,返回字符串表示的方法等.
不幸的是,似乎可能只扩展有区别的联合,但不可能扩展简单的枚举:
1.内在延伸
// CustomEnum.fs
module CustomEnumModule
type CustomEnum =
| Value1 = 1
| Value2 = 2
// Trying to split definition of the enum
type CustomEnum with
| Value3 = 3
Run Code Online (Sandbox Code Playgroud)
错误:"错误FS0010:意外符号'|' 在成员定义中"
2.可选扩展
// CustomEnumEx.fs
open CustomEnumModule
type CustomEnum with
member public x.PrintValue() =
printfn "%A" x
Run Code Online (Sandbox Code Playgroud)
错误:"错误FS0896:枚举不能包含成员"
对我来说这似乎很奇怪,因为(1)我们可以将简单的枚举视为全功能区分联合的一个特例,我们可以扩展有区别的联合和(2)扩展.NET枚举是添加一些功能的好方法(包括FP-功能)到现有的基础设施.
这种行为是故意的还是实施中的简单错误?
PS不幸的是F#Spec在这方面是沉默的,或者至少我找不到任何一种或那种行为的证据.