如果T为int,拳击会在这里å‘生å—?

时间:2019-01-17 16:35:25

标签: c# boxing

public static bool Equal<T>(T value, T match) {
           return Equals(value, match);
       }

所以问题是,如果T是int32,那么这里是å¦å°†è£…箱,å¦åˆ™ç¼–译器将选择ä¸è£…箱的int32等于?

1 个答案:

答案 0 :(得分:6)

对原始问题和Rango(基本正确)的回答在评论中有些混乱,所以我想我将其清除。

首先,有关泛型在C#中的工作方å¼çš„说明。泛型ä¸æ˜¯æ¨¡æ¿ï¼

在C#中,泛型一次由C#编译器编译为泛型IL,然åŽé€šè¿‡æŠ–动将ILé‡æ–°ç¼–译为特殊形å¼ã€‚例如,如果我们有一个方法M<T>(T t),那么C#编译器将将该方法åŠå…¶ä¸»ä½“一次编译为IL。

当出现抖动时,对M<string>,M<object>或M<IEnumerable>的调用将触å‘一个精确的编译;抖动éžå¸¸èªæ˜Žï¼Œå¹¶ä¸”åªè¦ç±»åž‹å®žå‚是引用类型,它就å¯ä»¥å°†ä¸»ä½“编译æˆå¯ä»¥å·¥ä½œçš„å½¢å¼ï¼Œè€Œä¸ç®¡ç±»åž‹å®žå‚是什么。但是M<int>å’ŒM<double>都将被编译æˆè‡ªå·±çš„汇编代ç ä¸»ä½“。

请注æ„,抖动ä¸çŸ¥é“C#的规则,并且C#å¯ä»¥è¿›è¡Œè¿‡è½½è§£æžã€‚到C#编译器生æˆIL时,已ç»ä¸ºæ¯ä¸ªæ–¹æ³•è°ƒç”¨é€‰æ‹©äº†ç¡®åˆ‡çš„方法。因此,如果您有:

static bool X(object a, object b) => object.Equals(a, b);
static bool X(int a, int b) => a == b;
static bool M<T>(T v, T m) => X(v, m);

然åŽé‡è½½åˆ†è¾¨çŽ‡é€‰æ‹©X(object, object)并åƒç¼–写代ç ä¸€æ ·ç¼–译代ç ï¼š

static bool M<T>(T v, T m) => X((object)v, (object)m);

如果事实è¯æ˜ŽT是int,则两个int都装在object上。

让我å†æ¬¡å¼ºè°ƒä¸€ä¸‹ã€‚到抖动时,我们已ç»çŸ¥é“将调用哪个X了;该决定是在C#编译时åšå‡ºçš„。 C#编译器的原因是“我有两个T,我ä¸çŸ¥é“它们å¯ä»¥è½¬æ¢ä¸ºint,所以我必须选择对象版本â€ã€‚

这与C ++模æ¿ä»£ç ç›¸å,åŽè€…为æ¯ä¸ªæ¨¡æ¿å®žä¾‹é‡æ–°ç¼–译该代ç ï¼Œå¹¶é‡æ–°æ‰§è¡Œé‡è½½è§£æžã€‚

这样å¯ä»¥å›žç­”最åˆæ出的问题。

现在让我们进入奇怪的细节。

  

在jit编译M<int>时,是å¦å…许抖动注æ„到M<int>调用X(object, object),然åŽè°ƒç”¨object.Equals(object, object),已知struct S { public int x; public void M() { this.x += 1; } } 比较两个装箱的整数是å¦ç›¸ç­‰ï¼Œå¹¶ç›´æŽ¥ç”Ÿæˆå°†ä¸¤ä¸ªæ•´æ•°ä»¥æœªè£…箱形å¼è¿›è¡Œæ¯”较的代ç ï¼Ÿ

是的,å…许执行该优化。

  

实际上å¯ä»¥æ‰§è¡Œä¼˜åŒ–å—?

æ®æˆ‘所知。抖动确实执行了一些内è”优化,但是æ®æˆ‘所知,它没有执行任何高级的内è”。

  

在实践中是å¦å­˜åœ¨æŠ–动导致拳击失败的情况?

是的ï¼

  

你能举一些例å­å—?

当然。考虑以下糟糕代ç ï¼š

S s = whatever;
s.M();

当我们这样åšæ—¶ï¼š

this

会å‘生什么?值类型中的ref S等效于类型sçš„å‚数。因此,我们对M进行了引用,并将其传递给interface I { void M(); } struct S : I { /* body as before */ } ,ä¾æ­¤ç±»æŽ¨ã€‚

现在考虑以下几点:

S s = whatever;
I i = s;
i.M();

现在å‡è®¾æˆ‘们这样åšï¼š

s

会å‘生什么?

  • å°†I转æ¢ä¸ºI是装箱转æ¢ï¼Œå› æ­¤æˆ‘们分é…一个盒å­ï¼Œä½¿è¯¥ç›’å­å®žçŽ°s,并在该盒å­ä¸­å¤åˆ¶i.M()。
  • 调用I会将框作为接收方传递到框中的s的实现中。然åŽï¼Œå°†å¼•ç”¨å¤åˆ¶åˆ°æ¡†ä¸­çš„this,并将引用作为M传递到void Q<T>(T t) where T : I { t.M(); } ... S s = whatever; Q<S>(s); 。

好å§ï¼ŒçŽ°åœ¨æ¥äº†ï¼Œè¿™å°†ä½¿æ‚¨å›°æƒ‘。

s

现在会å‘生什么?显然,我们将t的副本å¤åˆ¶åˆ°S中,没有装箱;两者å‡ä¸ºI.M类型。但是:I期望类型为t的接收者,而S的类型为t。我们必须åšä»¥å‰åšè¿‡çš„事å—?我们是å¦å°†I装到实现S.Mçš„ç›’å­ä¸­ï¼Œç„¶åŽè¯¥ç›’å­è°ƒç”¨this,其中S.M是对该盒å­çš„引用?

ä¸ã€‚抖动会生æˆä»£ç ï¼Œè¯¥ä»£ç å°†å–消装箱并直接以ref t作为thisçš„æ–¹å¼è°ƒç”¨void Q<T>(T t) where T : I { t.M(); } 。

这是什么æ„æ€ï¼Ÿè¿™æ„味ç€ï¼š

void Q<T>(T t) where T : I
{
  I i = t;
  i.M();
}

和

t

ä¸ä¸€æ ·ï¼å‰è€…çªå˜S,因为跳过了拳击。åŽé¢çš„框然åŽå¯¹æ¡†è¿›è¡Œå˜å¼‚。

这里的é‡ç‚¹åº”该是å¯å˜å€¼ç±»åž‹æ˜¯çº¯é‚ªæ¶çš„,您应该ä¸æƒœä¸€åˆ‡ä»£ä»·é¿å…使用它们。正如我们所看到的,您å¯ä»¥å¾ˆå®¹æ˜“地进入认为您应该对副本进行å˜å¼‚的情况,但您正在对原始å˜å¼‚进行å˜å¼‚,或更糟的是,您认为您对原始物进行å˜å¼‚,但是您正在å˜å¼‚一个副本。å¤åˆ¶ã€‚

  

什么奇异的魔法使它起作用?

使用sharplab.io并将我给出的方法å汇编为IL。仔细阅读IL。如果您ä¸äº†è§£ä»»ä½•å†…容,请查找。充分è¯æ˜Žäº†ä½¿æ­¤ä¼˜åŒ–有效的所有神奇机制。

  

抖动总是这样åšå—?

ä¸ï¼ (您会知é“是å¦æŒ‰ç…§æˆ‘的建议阅读了所有文档。)

但是,构建无法执行优化的方案有些棘手。我会把它当作一个难题:

给我写一个程åºï¼Œå…¶ä¸­æˆ‘们有一个实现接å£I的结构类型T。我们将类型å‚æ•°I约æŸä¸ºT,并用S构造T t,并传入t。我们用class C<T> { public virtual void M<U>(T t, U u) where U : T { } } class D : C<int> { public override void M<U>(int t, U u) { 调用一个方法作为接收方,而抖动总是使接收方被装箱。

æ示:我预计被调用方法的å称中包å«ä¸ƒä¸ªå­—æ¯ã€‚我说的对å—?

挑战#2:一个问题:是å¦æœ‰å¯èƒ½ä½¿ç”¨æˆ‘之å‰å»ºè®®çš„相åŒæŠ€æœ¯æ¥è¯æ˜Žæ‹³å‡»å‘生? (该技术是:表明拳击一定是已ç»å‘生的,因为副本而ä¸æ˜¯åŽŸå§‹å‰¯æœ¬å‘生了çªå˜ã€‚

  

是å¦å­˜åœ¨ä¸å¿…è¦çš„抖动盒情况?

是的ï¼å½“我在编译器上工作时,抖动并没有优化“从T到O的框,立å³å–消从O到T的框â€çš„指令åºåˆ—,有时C#编译器需è¦ç”Ÿæˆæ­¤ç±»åºåˆ—以使验è¯è€…满æ„。我们è¦æ±‚实施优化;我ä¸çŸ¥é“有没有。

  

你能举个例å­å—?

好的。å‡è®¾æˆ‘们有

U

好的,现在您知é“int的唯一å¯èƒ½ç±»åž‹æ˜¯t,因此u应该å¯åˆ†é…ç»™uå’Œ{{1 }}应该å¯åˆ†é…ç»™t,对å—?但是CLR验è¯ç¨‹åºä¸ä¼šé‚£æ ·çœ‹ï¼Œå› æ­¤æ‚¨å¯èƒ½ä¼šé‡åˆ°ç¼–译器必须生æˆå°†int装箱到object然åŽå†æ‹†ç®±åˆ°U的代ç çš„情况,是int,所以往返是没有æ„义的。

  

这是什么?

  • 请勿更改值类型。
  • 泛型ä¸æ˜¯æ¨¡æ¿ã€‚é‡è½½è§£æžä»…å‘生一次。
  • 抖动很难消除泛型中的装箱现象,但是如果将T转æ¢ä¸ºobject,那么该T便会真正转æ¢ä¸ºobject。