主頁 > .NET開發 > .Neter所應該徹底了解的委托

.Neter所應該徹底了解的委托

2020-09-21 02:16:44 .NET開發

 

本文將通過引出幾個問題來,并且通過例子來剖析C#中的委托以及用法,做拋磚引玉的作用

對于委托我發現大部分人都有以下問題,或者可能在面試中遇過這樣的:

  • 委托是不是相當于C/C++的函式指標?
  • 委托究竟是什么?
  • 委托究竟是用來干嘛的?
  • 委托跟匿名函式的區別?
  • 委托與事件的關系?


我們先來宣告和使用C++的函式指標:
代碼如下:

#include <iostream>
using namespace std;

typedef int (*Foohandle)(int a,int b);

int fooMenthod(int a, int (*foohandle1)(int a,int b)) //回呼函式
{
  return a + (*foohandle1)(2,3);//也可以寫成foohandle1(2,3)
}

int add(int a,int b) {
  return a + b;
}

int multiply(int a, int b) {
  return a * b;
}

int main()
{
 Foohandle foohandle = add;
 int (*foohandle1)(int a, int b) = &add;
 cout << foohandle(2,3)<<endl;
 cout << foohandle1(2,3) << endl;
 cout << typeid(Foohandle).name() << endl;
 cout << typeid(foohandle).name()<<endl;
 cout << typeid(foohandle1).name() << endl;
 cout << fooMenthod(2, add)<<endl;
 cout << fooMenthod(2, multiply);
}

輸出結果如下:

     

 

 

 

 

    在代碼中,我宣告定義了兩個函式add和multiply,然后用typedef方式宣告了函式指標,接著我分別將add賦值給Foohandle這種函式指標型別foohandle變數,然后用&add這種解地址的方式賦值給一個回傳值為int,且帶有兩個引數的函式指標foohandle1,其中(*foohandle1)是函式名,最后我輸出發現它們型別和輸出都是一致的,再后面,我們定義了一個fooMenthod函式,回傳值是int,且其中一個引數是函式指標,那么我再最后呼叫兩次,分別將add和multiply函式,賦值給它,這時候add和multiply就是fooMenthod函式的回呼函式,且此時輸出結果會被兩個函式內部不同實作所影響
那么我們可以做個總結:

  • 首先函式指標就是一個記憶體地址,指向函式的入口記憶體地址
  • 當函式指標做一個函式的引數時,確實會起到一定解耦作用
  • 函式指標很明顯是型別不安全的

我們再來宣告和使用委托:

public delegate int Foohandle(int a, int b);
class Program
{
static void Main(string[] args)
{
 Foohandle foohandle = new Foohandle(add);
 Console.WriteLine(foohandle(2, 3));
 Console.WriteLine(foohandle.GetType().Name);
 Console.WriteLine(fooMenthod(2, add));
 Console.WriteLine(fooMenthod(2, multiply));
 Console.WriteLine($"foohandle所呼叫函式函式名:{foohandle.Method.Name}");
 Console.WriteLine($"foohandle所呼叫函式的回傳值型別{foohandle.Method.ReturnType.ToString()}");
 Console.WriteLine("foohandle所呼叫函式引數型別以及引數名分別為:");
 Console.WriteLine($"Type:{foohandle.Method.GetParameters()[0].ParameterType},Name:{foohandle.Method.GetParameters()[0].Name}");
 Console.WriteLine($"Type:{foohandle.Method.GetParameters()[1].ParameterType},Name:{foohandle.Method.GetParameters()[1].Name}");
 Console.Read();
}

static int fooMenthod(int a, Foohandle foohandle) //傳給引數函式的就是回呼函式
{
 return a + foohandle(2, 3);
}

static int add(int a, int b)
{
 return a + b;
}

static int multiply(int a, int b)
{
 return a * b;
}
}

輸出結果:

   

 

 

 

 

     

    很明顯,不管是宣告和使用方式,都和c++那邊一樣,就連輸出結果也差不多,但是很有意思的是,foohandle的型別是Foohandle,且我居然能從foohandle輸出所調函式的一切資訊,包括函式名,回傳值,引數型別和引數名,而且和c++那邊不同的是,我們沒有直接操作記憶體地址,好像看起來是安全的?那么Foohandle型別又是什么?

委托是啥?

先來個例子:

namespace DelegateSample
{

  public delegate void FooHandle(int value);
class Program { static void Main(string[] args) { FooHandle fooHandle = new FooHandle(multiply); fooHandle(3); Console.WriteLine($"fooHandle.Target:{fooHandle.Target},fooHandle.Method:{fooHandle.Method},fooHandle.InvocationListCount:{fooHandle.GetInvocationList().Count()}"); Console.WriteLine("-----------------------------------------------------------------------------------------------------------------------------------"); FooHandle fooHandle1 = new FooHandle(new Foo().Add); fooHandle1.Invoke(3); Console.WriteLine($"fooHandle1.Target:{fooHandle1.Target},fooHandle1.Method:{fooHandle1.Method},fooHandle1.InvocationListCount:{fooHandle1.GetInvocationList().Count()}"); Console.Read(); } static void multiply(int a) { Console.WriteLine(a*2); } } public class Foo { public void Add(int value) { Console.WriteLine(value + 2); } } }

我們看看輸出的結果:

     很明顯,這里是一個最簡單的委托宣告,實體化初始化一個委托物件,然后呼叫的最簡單的場景
     我們不關注輸出的第一行,很明顯,物件實體化后,可以訪問其中的三個公開public的函式成員,
分別是Target(object型別),Method(MethodInfo型別),而GetInvocationList函式是一個回傳值為一個Delegate[]的無參函式
     在上面代碼,其實我還特地將委托FooHandle宣告在Program類外面,其實在這里我們已經知道委托是什么了,實體化物件,且能夠宣告在類外面,其實它本質就是一個類,我們通過反編譯來驗證:

 

 

 

 

 

 

大概是這樣,偽代碼如下:

public class FooHandle: MulticastDelegate
{
  public FooHandle(object @object,IntPtr menthod);//構造方法

  void Invoke(int value)//呼叫委托,編譯后公共語言運行時給delegate提供的特殊方法

  void EndInvoke(System.IAsyncResult asyncResult)// 編譯后公共語言運行時給MulticastDelegate提供的特殊方法

  // 編譯后公共語言運行時給MulticastDelegate提供的特殊方法
  void BeginInvoke(int value,System.AsyncCallback callback, object obj) 
}

 

我們可以看編譯后FooHandle就是一個類,且繼承MulticastDelegate,且繼承鏈關系在msdn是這樣的:



   

   

    且我們發現上面公開的三個函式成員都來自于Delegate類,且編譯后生成了幾個公共運行時提供的特殊方法,Invoke方法我們很清楚,是來呼叫委托的,我們先來看看委托初始化后的情況,通過查看Delegate的原始碼,我們發現Delegate有兩個建構式:

1.委托物件初始化建構式是實體函式:

[SecuritySafeCritical]
protected Delegate(object target, string method)
{
  if (target == null)
  {
    throw new ArgumentNullException("target");
  }
  if (method == null)
  {
    throw new ArgumentNullException("method");
  }
  if (!BindToMethodName(target, (RuntimeType)target.GetType(), method, (DelegateBindingFlags)10))
  {
    throw new ArgumentException(Environment.GetResourceString("Arg_DlgtTargMeth"));
  }
} 

 

2.委托物件初始化建構式是靜態函式:

[SecuritySafeCritical]
protected Delegate(Type target, string method)
{
  if (target == null)
  {
    throw new ArgumentNullException("target");
  }
  if (target.IsGenericType && target.ContainsGenericParameters)
  {
    throw new ArgumentException(Environment.GetResourceString("Arg_UnboundGenParam"), "target");
  }
  if (method == null)
  {
    throw new ArgumentNullException("method");
  }
  RuntimeType runtimeType = target as RuntimeType;
  if (runtimeType == null)
  {
    throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeType"), "target");
  }
  BindToMethodName(null, runtimeType, method, (DelegateBindingFlags)37);
}

最后共同呼叫的方法:

//呼叫CLR的內部代碼
[MethodImpl(MethodImplOptions.InternalCall)]
[SecurityCritical]
private extern bool BindToMethodName(object target, RuntimeType methodType, string method, DelegateBindingFlags flags);

    雖然我們看不到BindToMethodName方法的實作,已經很明顯了,委托物件初始化建構式是靜態函式傳參進去BindToMethodName的第一個object的target引數為null,那我們大概把之前的偽代碼的建構式這么實作了:

偽代碼部分:

internal object _target//目標物件;
internal IntPtr _methodPtr//目標方法;
internal IntPtr _methodPtrAux//用來判斷Target是否為空;

//foolHandle的構造方法實作:
public FooHandle(object @object,IntPtr menthod)
{
  _methodPtr=menthod;//multiply
  _methodPtrAux=1;//只要不等于nul

}

//foolHandle1的構造方法實作:
public FooHandle(object @object,IntPtr menthod)
{
  _methodPtr=menthod//Add
  _methodPtrAux=0//為null
  _target=foo;

}

 

Delegate Target屬性源代碼部分:

[__DynamicallyInvokable]
public object Target
{
  [__DynamicallyInvokable]
  get
  {
    return GetTarget();
  }
}

[SecuritySafeCritical]
internal virtual object GetTarget()
{
  if (!_methodPtrAux.IsNull())
  {
    return null;
  }
  return _target;
}

    而獲取Method的方法就不展開了,就是通過反射來獲取,那我們已經知道Target和Method屬性究竟是怎么回事了,我們還發現沒講到GetInvocationList方法是怎么回事?我們知道委托是支持多播委托的,也就是大概這樣,修改上述代碼為:

namespace DelegateSample
{
   public delegate void FooHandle(int value);
class Program { static void Main(string[] args) { FooHandle fooHandle = new FooHandle(multiply); fooHandle(3); Console.WriteLine($"fooHandle.Target:{fooHandle.Target},fooHandle.Method:{fooHandle.Method},fooHandle.InvocationListCount:{fooHandle.GetInvocationList().Count()}"); Console.WriteLine("----------------------------------------------------------------------------------------------------------------"); FooHandle fooHandle1 = new FooHandle(new Foo().Add); fooHandle1.Invoke(3); Console.WriteLine($"fooHandle1.Target:{fooHandle1.Target},fooHandle1.Method:{fooHandle1.Method},fooHandle1.InvocationListCount:{fooHandle1.GetInvocationList().Count()}"); Console.WriteLine(); Console.WriteLine("--------------------------------------------------新增代碼------------------------------------------------------"); FooHandle fooHandle2 = new FooHandle(new Program().Minus); Console.WriteLine($"fooHandle2.Target:{fooHandle2.Target},fooHandle1.Method:{fooHandle2.Method},fooHandle1.InvocationListCount:{fooHandle2.GetInvocationList().Count()}"); fooHandle2(2); Console.WriteLine("----------------------------------------------------------------------------------------------------------------"); FooHandle fooHandle3 = null; fooHandle3 += fooHandle; fooHandle3 =(FooHandle)Delegate.Combine(fooHandle3,fooHandle1);//相當于fooHandle3+=fooHandle1; fooHandle3 += new Program().Minus; Console.WriteLine($"fooHandle3.Target:{fooHandle3.Target},fooHandle3.Method:{fooHandle3.Method},fooHandle3.InvocationListCount:{fooHandle3.GetInvocationList().Count()}"); fooHandle3(2); foreach (var result in fooHandle3.GetInvocationList()) { Console.WriteLine($"result.Target:{result.Target},result.Method:{result.Method},result.InvocationListCount:{result.GetInvocationList().Count()}"); } Console.Read(); } private void Minus(int a) { Console.WriteLine(a-1); } static void multiply(int a) { Console.WriteLine(a * 2); } } public class Foo { public void Add(int value) { Console.WriteLine(value + 2); } } }

輸出結果是:

    上面新增的代碼,我宣告了一個新的委托變數fooHandle3初始化為null,接著分別用三種不同的方式將委托或者函式加給fooHandle,之后輸出后相當于分別按序呼叫輸出了三個方法,而我們遍歷其中的fooHandle3.GetInvocationList()委托陣列,輸出的也確實三個方法,但是注意到了沒,我在fooHandle3 += new Program().Minus這段確實沒有宣告一個委托變數,我們可以注意到其中的(FooHandle)Delegate.Combine(fooHandle3,fooHandle1)這句,Combine很明顯是需要兩個委托變數的,查看編譯后的代碼我們可以得知到底發生了啥?
Il關鍵代碼如下:

//fooHandle3 += fooHandle
IL_00f7: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate,
class [mscorlib]System.Delegate)
IL_00fc: castclass DelegateSample.FooHandle
IL_0101: stloc.3
IL_0102: ldloc.3
IL_0103: ldloc.1
//fooHandle3 =(FooHandle)Delegate.Combine(fooHandle3,fooHandle1)
IL_0104: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate,
class [mscorlib]System.Delegate)
IL_0109: castclass DelegateSample.FooHandle
IL_010e: stloc.3
IL_010f: ldloc.3
//new Program()
IL_0110: newobj instance void DelegateSample.Program::.ctor()
IL_0115: ldftn instance void DelegateSample.Program::Minus(int32)
//new FooHandle()新增了一個FooHandle委托變數
IL_011b: newobj instance void DelegateSample.FooHandle::.ctor(object,
native int)
//fooHandle3 += new Program().Minus 
IL_0120: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate,
class [mscorlib]System.Delegate)

     也就是三種不同方式都會被翻譯為Combine方法,如果是直接+=函式這種情況,后臺也會new一個委托變數,將方法賦值給該變數再加到fooHandle3,那么我們可以知道,最關鍵的核心代碼就應該是Delegate.combine這個靜態方法了,我們來看看原始碼是怎么回事:
Delegate類的:

[__DynamicallyInvokable]
public static Delegate Combine(Delegate a, Delegate b)
{
   if ((object)a == null)
   {
     return b;
   }
   return a.CombineImpl(b);
}

protected virtual Delegate CombineImpl(Delegate d)
{
   throw new MulticastNotSupportedException(Environment.GetResourceString("Multicast_Combine"));
}

 

MulticastDelegate類的:

[SecurityCritical]
private object _invocationList;//委托鏈表

[SecurityCritical]
private IntPtr _invocationCount;

[SecuritySafeCritical]
protected sealed override Delegate CombineImpl(Delegate follow)
{
   if ((object)follow == null)
   {
      return this;
   }
   if (!Delegate.InternalEqualTypes(this, follow))
   {
     throw new ArgumentException(Environment.GetResourceString("Arg_DlgtTypeMis"));
   }
   MulticastDelegate multicastDelegate = (MulticastDelegate)follow;
   int num = 1;
   object[] array = multicastDelegate._invocationList as object[];
   if (array != null)
   {
       num = (int)multicastDelegate._invocationCount;
   }
   object[] array2 = _invocationList as object[];
   int num2;
   object[] array3;
   if (array2 == null)
   {
     num2 = 1 + num;
     array3 = new object[num2];
     array3[0] = this;
     if (array == null)
     {
        array3[1] = multicastDelegate;
     }
     else
     {
       for (int i = 0; i < num; i++)
       {
         array3[1 + i] = array[i];
       }
     }
     return NewMulticastDelegate(array3, num2);
    }
    int num3 = (int)_invocationCount;
    num2 = num3 + num;
    array3 = null;
    if (num2 <= array2.Length)
    {
       array3 = array2;
       if (array == null)
       {
           if (!TrySetSlot(array3, num3, multicastDelegate))
           {
             array3 = null;
           }
       }
       else
       {
         for (int j = 0; j < num; j++)
         {
            if (!TrySetSlot(array3, num3 + j, array[j]))
            {
               array3 = null;
               break;
            }
         }
       }
    }
    if (array3 == null)
    {
       int num4;
       for (num4 = array2.Length; num4 < num2; num4 *= 2)
       {
       }
       array3 = new object[num4];
       for (int k = 0; k < num3; k++)
       {
          array3[k] = array2[k];
       }
       if (array == null)
       {
          array3[num3] = multicastDelegate;
       }
       else
       {
          for (int l = 0; l < num; l++)
          {
             array3[num3 + l] = array[l];
          }
       }
     }
     return NewMulticastDelegate(array3, num2, thisIsMultiCastAlready: true);
   }

 

GetInvocationList方法的實作:

//Delgate類的
public virtual Delegate[] GetInvocationList()
{
   return new Delegate[1]
   {
     this
   };
}

//MulticastDelegate類的
public sealed override Delegate[] GetInvocationList()
{
   object[] array = _invocationList as object[];
   Delegate[] array2;
   if (array == null)
   {
     array2 = new Delegate[1]
     {
       this
     };
   }
   else
   {
     int num = (int)_invocationCount;
     array2 = new Delegate[num];
     for (int i = 0; i < num; i++)
     {
        array2[i] = (Delegate)array[i];
     }
   }
   return array2;
}

 

    其實我們看到這里,就可以知道其中的一個最主要就是_invocationList變數,也就是當呼叫Combine的時候,會判斷左邊委托變數是否為空,如果為空,會回傳右邊的委托變數,不為空就會呼叫CombineImpl方法,以上面那個例子來說fooHandle3_invocationList存盤著所有附加到委托變數,包含物件本身,也就是為啥遍歷fooHandle3.GetInvocationList,輸出了三個附加到fooHandle3變數的委托變數,這里例子fooHandle3初始化為null,還有意思的是fooHandle3的Targt和Menthod屬性是最后附加的那個委托變數的Target和Menthod,而當委托由回傳值,也同理回傳最后一個函式的回傳值,那么fooHandle3大概的結構如下圖:

 

     我們到現在只用到+=,其實-=就是呼叫其Delegate.Remove方法,跟Combine方法作用相反,具體就不多概述
看到這里我們終于可以回答一開頭拋出的幾個問題?

  • 委托是不是相當于C/C++的函式指標?

          很明顯,不是的,從資料結構來說,c++函式指標表示一塊指向函式的記憶體地址,它其實和直接寫函式名沒啥區別,因為我們呼叫函式時的函式名,也是函式入口地址,而委托卻是個類,是一塊托管記憶體,使用Invoke后它就會被clr釋放了,它的函式成員能夠存盤所調函式的所有資訊,這是函式指標沒做到的,但是在某些特殊情況下,C++的函式指標就和委托一樣,有興趣的朋友可以去看下p/invoke方面知識

  • 委托是什么?

          委托本質是類,且支持多播委托的本質是維護一個私有的_invocationList委托鏈物件,+=和-=都是呼叫其靜態方法Combine和Remove

  • 委托是用來做啥的?

          委托和c++函式指標一樣,都可以作為函式中轉器,在呼叫者和被呼叫者中起解耦作用,可作為函式的引數,當回呼函式

  • 委托跟匿名函式的區別?

         我們先來宣告和使用匿名函式:

public delegate int Foohandle(int a, int b);

Foohandle foohandle = delegate (int a, int b) { return a + b; };//匿名方法方式
Foohandle foohandle1= (a, b)=> a + b;//Lambda 運算式方式

foohandle.Invoke(2,2);//輸出4
foohandle1.Invoke(2,2);//輸出4

 

我們來看下msdn是怎么定義匿名函式的:

 

很明顯,匿名函式只是個運算式,可以用來初始化委托的,而委托是個類,其實通過查看IL,后臺都會實體化一個新的委托物件,并把該運算式作為函式賦給它

  • 委托與事件的關系?

 同樣的我們來宣告和使用事件:

public class Foo
{
   public delegate void Foohandel(int a, int b);

   public event Foohandel foohandle;

   public Foo()
   {
      foohandle = new Foohandel(add);
      foohandle(2,2);//在Foo里面可以直接呼叫事件
      Console.WriteLine($"{foohandle.Target},{foohandle.Method}");
   }

   public void excute(int a,int b)//公開給外部類呼叫事件的函式
   {
      foohandle?.Invoke(a,b);
   }

   private void add(int a, int b)
   {
      Console.WriteLine(a + b); 
   }
}

class Program
{
   static void Main(string[] args)
   {
      Foo foo = new Foo();
      //foo.foohandle = new Foo.Foohandel(multiply);編譯不過,提示foo.foohandle只能出現再+=和-=左邊
      foo.foohandle +=new Foo.Foohandel(multiply);
      foo.excute(2, 2); 
      Console.Read();
   }

   static void multiply(int a,int b)
   {
      Console.WriteLine(a * b); 
   }
}

 

輸出結果:

4
EventSample.Foo,Void add(Int32, Int32)
4
4

 

     我們發現,在Foo類里面,事件foohandle就是相當于委托,但是在外部,我們再program的main函式訪問它時候,我們發現foohandle只能做+=或者-=,也不能訪問其函式成員Target和Menthod,而我們只能通過呼叫excute函式去呼叫,這時候我們可以知道,Event其實是基于委托的,在內部類相當于委托,在外部就只能有委托的多播功能,其余都不能訪問,其實我們想到,屬性是不是這樣,,,有興趣的朋友可以去了解事件的原理,也是很有趣


最后的最后,我們還要談下委托的一個功能:

委托的引數逆變和回傳值的協變

由于委托也支持泛型委托,因此我們可以看看微軟定義好的

public delegate void Action<in T>(T obj);//其中in表示逆變
public delegate TResult Func<out TResult>();//其中out表示協變

class Program
{
    static Action<object> action;
    static Func<string> func;
    static void Main(string[] args)
    {
       action = (object a) => { Console.WriteLine(a.ToString()); };
       Action<string> action1 = action;//引數逆變
       action("Hello!");


       func = () => { return "I am Func"; };
       Func<object> func1 = func;//回傳值協變
       Console.WriteLine(func1()); 
       Console.ReadLine();
    }

}

 

輸出結果:

Hello!
I am Func

想要了解更深的朋友可以去了解泛型的協變和逆變,在這里就不深入探討了

 

     

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/93249.html

標籤:C#

上一篇:基于Win服務的標簽列印(模板套打)

下一篇:C#程式撰寫高質量代碼改善的157個建議【16-19】[動態陣列、回圈遍歷、物件集合初始化]

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • WebAPI簡介

    Web體系結構: 有三個核心:資源(resource),URL(統一資源識別符號)和表示 他們的關系是這樣的:一個資源由一個URL進行標識,HTTP客戶端使用URL定位資源,表示是從資源回傳資料,媒體型別是資源回傳的資料格式。 接下來我們說下HTTP. HTTP協議的系統是一種無狀態的方式,使用請求/ ......

    uj5u.com 2020-09-09 22:07:47 more
  • asp.net core 3.1 入口:Program.cs中的Main函式

    本文分析Program.cs 中Main()函式中代碼的運行順序分析asp.net core程式的啟動,重點不是剖析原始碼,而是理清程式開始時執行的順序。到呼叫了哪些實體,哪些法方。asp.net core 3.1 的程式入口在專案Program.cs檔案里,如下。ususing System; us ......

    uj5u.com 2020-09-09 22:07:49 more
  • asp.net網站作為websocket服務端的應用該如何寫

    最近被websocket的一個問題困擾了很久,有一個需求是在web網站中搭建websocket服務。客戶端通過網頁與服務器建立連接,然后服務器根據ip給客戶端網頁發送資訊。 其實,這個需求并不難,只是剛開始對websocket的內容不太了解。上網搜索了一下,有通過asp.net core 實作的、有 ......

    uj5u.com 2020-09-09 22:08:02 more
  • ASP.NET 開源匯入匯出庫Magicodes.IE Docker中使用

    Magicodes.IE在Docker中使用 更新歷史 2019.02.13 【Nuget】版本更新到2.0.2 【匯入】修復單列匯入的Bug,單元測驗“OneColumnImporter_Test”。問題見(https://github.com/dotnetcore/Magicodes.IE/is ......

    uj5u.com 2020-09-09 22:08:05 more
  • 在webform中使用ajax

    如果你用過Asp.net webform, 說明你也算是.NET 開發的老兵了。WEBform應該是2011 2013左右,當時還用visual studio 2005、 visual studio 2008。后來基本都用的是MVC。 如果是新開發的專案,估計沒人會用webform技術。但是有些舊版 ......

    uj5u.com 2020-09-09 22:08:50 more
  • iis添加asp.net網站,訪問提示:由于擴展配置問題而無法提供您請求的

    今天在iis服務器配置asp.net網站,遇到一個問題,記錄一下: 問題:由于擴展配置問題而無法提供您請求的頁面。如果該頁面是腳本,請添加處理程式。如果應下載檔案,請添加 MIME 映射。 WindowServer2012服務器,添加角色安裝完.netframework和iis之后,運行aspx頁面 ......

    uj5u.com 2020-09-09 22:10:00 more
  • WebAPI-處理架構

    帶著問題去思考,大家好! 問題1:HTTP請求和回傳相應的HTTP回應資訊之間發生了什么? 1:首先是最底層,托管層,位于WebAPI和底層HTTP堆疊之間 2:其次是 訊息處理程式管道層,這里比如日志和快取。OWIN的參考是將訊息處理程式管道的一些功能下移到堆疊下端的OWIN中間件了。 3:控制器處理 ......

    uj5u.com 2020-09-09 22:11:13 more
  • 微信門戶開發框架-使用指導說明書

    微信門戶應用管理系統,采用基于 MVC + Bootstrap + Ajax + Enterprise Library的技術路線,界面層采用Boostrap + Metronic組合的前端框架,資料訪問層支持Oracle、SQLServer、MySQL、PostgreSQL等資料庫。框架以MVC5,... ......

    uj5u.com 2020-09-09 22:15:18 more
  • WebAPI-HTTP編程模型

    帶著問題去思考,大家好!它是什么?它包含什么?它能干什么? 訊息 HTTP編程模型的核心就是訊息抽象,表示為:HttPRequestMessage,HttpResponseMessage.用于客戶端和服務端之間交換請求和回應訊息。 HttpMethod類包含了一組靜態屬性: private stat ......

    uj5u.com 2020-09-09 22:15:23 more
  • 部署WebApi隨筆

    一、跨域 NuGet參考Microsoft.AspNet.WebApi.Cors WebApiConfig.cs中配置: // Web API 配置和服務 config.EnableCors(new EnableCorsAttribute("*", "*", "*")); 二、清除默認回傳XML格式 ......

    uj5u.com 2020-09-09 22:15:48 more
最新发布
  • C#多執行緒學習(二) 如何操縱一個執行緒

    <a href="https://www.cnblogs.com/x-zhi/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2943582/20220801082530.png" alt="" /></...

    uj5u.com 2023-04-19 09:17:20 more
  • C#多執行緒學習(二) 如何操縱一個執行緒

    C#多執行緒學習(二) 如何操縱一個執行緒 執行緒學習第一篇:C#多執行緒學習(一) 多執行緒的相關概念 下面我們就動手來創建一個執行緒,使用Thread類創建執行緒時,只需提供執行緒入口即可。(執行緒入口使程式知道該讓這個執行緒干什么事) 在C#中,執行緒入口是通過ThreadStart代理(delegate)來提供的 ......

    uj5u.com 2023-04-19 09:16:49 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    <a href="https://www.cnblogs.com/huangxincheng/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/214741/20200614104537.png" alt="" /&g...

    uj5u.com 2023-04-18 08:39:04 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    一:背景 1. 講故事 前段時間協助訓練營里的一位朋友分析了一個程式卡死的問題,回過頭來看這個案例比較經典,這篇稍微整理一下供后來者少踩坑吧。 二:WinDbg 分析 1. 為什么會卡死 因為是表單程式,理所當然就是看主執行緒此時正在做什么? 可以用 ~0s ; k 看一下便知。 0:000> k # ......

    uj5u.com 2023-04-18 08:33:10 more
  • SignalR, No Connection with that ID,IIS

    <a href="https://www.cnblogs.com/smartstar/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/u36196.jpg" alt="" /></a>...

    uj5u.com 2023-03-30 17:21:52 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:15:33 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:13:31 more
  • C#遍歷指定檔案夾中所有檔案的3種方法

    <a href="https://www.cnblogs.com/xbhp/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/957602/20230310105611.png" alt="" /></a&...

    uj5u.com 2023-03-27 14:46:55 more
  • C#/VB.NET:如何將PDF轉為PDF/A

    <a href="https://www.cnblogs.com/Carina-baby/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2859233/20220427162558.png" alt="" />...

    uj5u.com 2023-03-27 14:46:35 more
  • 武裝你的WEBAPI-OData聚合查詢

    <a href="https://www.cnblogs.com/podolski/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/616093/20140323000327.png" alt="" /><...

    uj5u.com 2023-03-27 14:46:16 more