簡體   English   中英

為什么必須使用動態輸入參數來強制轉換強類型函數?

[英]Why do I have to type-cast a strongly typed function using a dynamic input-parameter?

我有一個轉換器方法:

MyPoco Convert(dynamic p) => new MyPoco { A = p.X, B = p.Y };
void Test()
{
    dynamic item = new { X = 1, Y = 2 };
    var poco = (MyPoco)Convert(item);
}

我必須將結果明確地轉換為MyPoco,否則poco也將成為動態變量。

但是,如果我內聯Convert方法;

void Test()
{
    MyPoco Convert(dynamic p) => new MyPoco { A = p.X, B = p.Y };
    dynamic item = new { X = 1, Y = 2 };
    var poco = Convert(item);
}

我不需要將ConvertItem轉換為MyPoco。 有這種現象的原因嗎? 編譯器應該很容易知道Convert return-type是MyPoco,對嗎?

它們之間存在差異,這可能是原因-局部函數不支持重載。 我認為這很重要-假設我們有兩個名稱相同,輸入和輸出類型不同的方法

static void Main(string[] args)
{
    dynamic d = null;
    var result = Hello(d);
    Console.WriteLine("Hello World!");
}

static string Hello(string s)
{
    return s;
}

static int Hello(int i)
{
    return i;
}

這意味着結果可能是字符串或整數-我們在編譯時不知道。

而對於以下代碼,我們得到的錯誤是局部變量或函數已經聲明

static void Main(string[] args)
{
    string Hello(string s)
    {
        return s;
    }
    int Hello(int s) // error - local variable or function with the same name already declared
    {
        return s;
    }
    dynamic d = null;
    var result = Hello(d);
    Console.WriteLine("Hello World!");
}

我們只能這樣寫

static void Main(string[] args)
{
    string Hello(string s)
    {
        return s;
    }

    dynamic d = null;
    var result = Hello(d);
    Console.WriteLine("Hello World!");
}

因此,當編譯器看到本地Hello(...)調用時,它知道返回類型為字符串。

UPD:

關於編譯器在動態情況下推斷正確類型的能力。

我認為是的,編譯器可能會遇到這種情況-如果我們在編譯時知道只有一個方法,那么在運行時就不會出現另一個方法。

我可以想象,例如,我們調用的方法在另一個程序集中,並且在運行時加載了具有不同簽名的較新版本-動態可以使用,但是對於不帶重載的私有靜態方法,我認為我們可以推斷出非-動態類型。

但是我認為,為了簡單起見,決定以這種方式實現它-更容易記住簡單的規則-涉及動態的一切-動態。

為了簡化本地函數,我認為將它們動態化也將更加容易。 我認為這只是不同的人實施該方案的決定。

我檢查了roslyn源代碼,試圖找到有關此信息。

定義它的位置是Binder_Invocation.cs,BindMethodGroupInvocation方法。

對於局部函數,調用以下方法

private BoundExpression BindLocalFunctionInvocationWithDynamicArgument(
        SyntaxNode syntax,
        SyntaxNode expression,
        string methodName,
        BoundMethodGroup boundMethodGroup,
        DiagnosticBag diagnostics,
        CSharpSyntaxNode queryClause,
        MethodGroupResolution resolution)
    {
        // Invocations of local functions with dynamic arguments don't need
        // to be dispatched as dynamic invocations since they cannot be
        // overloaded. Instead, we'll just emit a standard call with
        // dynamic implicit conversions for any dynamic arguments. There
        // are two exceptions: "params", and unconstructed generics. While
        // implementing those cases with dynamic invocations is possible,
        // we have decided the implementation complexity is not worth it.
        // Refer to the comments below for the exact semantics.

如您所見,也有關於重載的說法,但是對於普通方法而言,沒有任何關於原因的信息

                    else
                    {
                        if (HasApplicableConditionalMethod(resolution.OverloadResolutionResult))
                        {
                            // warning CS1974: The dynamically dispatched call to method 'Goo' may fail at runtime
                            // because one or more applicable overloads are conditional methods
                            Error(diagnostics, ErrorCode.WRN_DynamicDispatchToConditionalMethod, syntax, methodGroup.Name);
                        }

                        // Note that the runtime binder may consider candidates that haven't passed compile-time final validation 
                        // and an ambiguity error may be reported. Also additional checks are performed in runtime final validation 
                        // that are not performed at compile-time.
                        // Only if the set of final applicable candidates is empty we know for sure the call will fail at runtime.
                        var finalApplicableCandidates = GetCandidatesPassingFinalValidation(syntax, resolution.OverloadResolutionResult,
                                                                                            methodGroup.ReceiverOpt,
                                                                                            methodGroup.TypeArgumentsOpt,
                                                                                            diagnostics);
                        if (finalApplicableCandidates.Length > 0)
                        {
                            result = BindDynamicInvocation(syntax, methodGroup, resolution.AnalyzedArguments, finalApplicableCandidates, diagnostics, queryClause);
                        }
                        else
                        {
                            result = CreateBadCall(syntax, methodGroup, methodGroup.ResultKind, analyzedArguments);
                        }

如果至少有一個候選人,他們會創造動態。 因此,正如我所說,我認為它可以是非動態的,但是實施它的人最初將其保持動態,可能是為了簡化。

為了找到更多細節,您可以做的是嘗試在沒有重載方法的情況下實現這種情況,更改代碼

if (finalApplicableCandidates.Length > 0)
{
    result = BindDynamicInvocation(syntax, methodGroup, resolution.AnalyzedArguments, finalApplicableCandidates, diagnostics, queryClause);
}

通過添加Length == 1的檢查,然后調用BindInvocationExpressionContinued而不是BindDynamicInvocation並運行測試並檢查是否有失敗,也許有幫助(我什至沒有設法建立roslyn項目,dotnet核心有點奇怪)

PS

根據這個

if (boundMethodGroup.TypeArgumentsOpt.IsDefaultOrEmpty && localFunction.IsGenericMethod)
{
    Error(diagnostics, ErrorCode.ERR_DynamicLocalFunctionTypeParameter, syntax, localFunction.Name);
    return BindDynamicInvocation(

對於局部函數,我們可以獲取動態而不是具體類型。

如果您輸入這樣的內容

static void Main(string[] args)
    {
        int TestFunc<T>(T data)
        {
            return 1;
        }

        dynamic d = 2;

        var r = TestFunc(d);
    }

是的,它將給出錯誤,但是如果您檢查推斷的類型,則r會顯示動態))

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM