IOC控制反轉

来源:https://www.cnblogs.com/taotaozhuanyong/archive/2019/09/21/11562082.html
-Advertisement-
Play Games

參考博客地址: http://www.cnblogs.com/qqlin/archive/2012/10/16/2717964.html https://www.cnblogs.com/lyps/p/10560256.html 這篇文章主要介紹.NET Framework下麵的IOC以及Unity的 ...


參考博客地址:

http://www.cnblogs.com/qqlin/archive/2012/10/16/2717964.html

https://www.cnblogs.com/lyps/p/10560256.html

這篇文章主要介紹.NET Framework下麵的IOC以及Unity的使用,下一篇文章介紹.NET Core下麵自帶的容器IServiceCollection以及Autofac的使用https://www.cnblogs.com/taotaozhuanyong/p/11562184.html

IOC(Inverse of Control),控制反轉。

說到IOC,就不得不提DI(Dependency Injection),依賴註入

IOC是目標效果,需要DI依賴註入的手段。

分層架構時這些是必須的,可以劃分邊界獨立演化,也方便分工,促進代碼復用。。

依賴倒置原則DIP:

  系統架構時,高層模塊不應該依賴於低層模塊,二者通過抽象來依賴。依賴抽象而不是依賴細節。在A勒種調用了B類,A類就是高層,B類就是低層。

面向抽象:

  1、一個方法滿足多種類型

  2、支持下層的擴展。

下麵有三種創建一個對象的方式:

AndroidPhone phone = new AndroidPhone();//1 全是細節

IPhone phone = new AndroidPhone();//2 左邊抽象右邊細節

IPhone phone = ObjectFactory.CreatePhone();//3 封裝轉移
/// <summary>
/// 簡單工廠+配置文件+反射
/// </summary>
public class ObjectFactory
{
    public static IPhone CreatePhone()
    {
        string classModule = ConfigurationManager.AppSettings["iPhoneType"];
        Assembly assemly = Assembly.Load(classModule.Split(',')[1]);
        Type type = assemly.GetType(classModule.Split(',')[0]);
        return (IPhone)Activator.CreateInstance(type);//無參數構造函數
    }

    public static IPhone CreatePhone(IBaseBll iBLL)
    {
        string classModule = ConfigurationManager.AppSettings["iPhoneType"];
        Assembly assemly = Assembly.Load(classModule.Split(',')[1]);
        Type type = assemly.GetType(classModule.Split(',')[0]);
        return (IPhone)Activator.CreateInstance(type, new object[] { iBLL });
    }
}

在App.config下麵配置節點:

<appSettings>
    <add key="iPhoneType" value="Bingle.Service.AndroidPhone,Bingle.Service" />
  </appSettings>

只有抽象,沒有細節,好處是可擴展。

IOC控制反轉:

  傳統開發,上端依賴(調用/指定)下端對象,這個樣子會有依賴。控制反轉就是把對下端對象的依賴轉移到第三方容器(工廠+配置文件+反射),能夠讓程式擁有更好的擴展性。

下麵出現一個問題:

  構造A對象,但是A依賴於B對象,那就先構造B,如果B又依賴C,再構造C。。。。。。

IDAL.IBaseDAL baseDAL = new Ruamou.DAL.BaseDAL();
IBLL.IBaseBll baseBll = new Ruanmou.BLL.BaseBll(baseDAL);
IPhone phone = ObjectFactory.CreatePhone(baseBll);

現在這個問題已經暴露了,下麵開始了DI,依賴註入,就能做到構造某個對象時,將依賴的對象自動初始化並註入。

IOC是目標效果,需要DI依賴註入的手段。

DI依賴註入:

  三種方式註入:構造函數註入、屬性註入、方法註入(按照時間順序)

  [Dependency]//屬性註入

 [Dependency]//屬性註入
 public IMicrophone iMicrophone { get; set; }

  [InjectionConstructor]//構造函數註入,預設找參數最多的構造函數,方法註入不加這個特性也是可以的,可以不用特性,可以去掉對容器的依賴

[InjectionConstructor]//構造函數註入:預設找參數最多的構造函數
public ApplePhoneUpdate(IHeadphone headphone)
{
    this.iHeadphone = headphone;
    Console.WriteLine("{0} 帶參數構造函數", this.GetType().Name);
}

  [InjectionMethod]//方法註入

 [InjectionMethod]//方法註入
 public void Init(IPower power)
 {
     this.iPower = power;
 }

如何使用Unity容器?

  1、安裝Unity

  

 

   2、容器三部曲:

    實例化容器、註冊類型、獲取實例

  3、項目版本和服務處的版本要一直。

下麵是Iphone與AndroidPhone的定義:

 public interface IPhone
 {
     void Call();
     IMicrophone iMicrophone { get; set; }
     IHeadphone iHeadphone { get; set; }
     IPower iPower { get; set; }
 }




 public class AndroidPhone : IPhone
 {
     public IMicrophone iMicrophone { get; set; }
     public IHeadphone iHeadphone { get; set; }
     public IPower iPower { get; set; }

     //public AndroidPhone()
     //{
     //    Console.WriteLine("{0}構造函數", this.GetType().Name);
     //}
     
     public AndroidPhone(AbstractPad pad, IHeadphone headphone)
     {
         Console.WriteLine("{0}構造函數", this.GetType().Name);
     }

     //[ElevenInjectionConstructor]
     public AndroidPhone(AbstractPad pad)
     {
         Console.WriteLine("{0}構造函數", this.GetType().Name);
     }
     public AndroidPhone(IBaseBll baseBll)
     {
         Console.WriteLine("{0}構造函數", this.GetType().Name);
     }

     public void Call()
     {
         Console.WriteLine("{0}打電話", this.GetType().Name); ;
     }
 }
View Code
 IUnityContainer container = new UnityContainer();//1 實例化容器
 container.RegisterType<IPhone, AndroidPhone>();//2 註冊類型
 IPhone iphone = container.Resolve<IPhone>();//3 獲取實例

我們來看一下Unity下麵RegisterType方法裡面的泛型約束:

 

還有一個方法:

 

 

IUnityContainer container = new UnityContainer();//1 實例化容器

container.RegisterInstance<AbstractPad>(new ApplePadChild());

AbstractPad abstractPad = container.Resolve<AbstractPad>();

後遭的時候,有依賴:

 

 下麵來解決這個問題:但是要保持Unity版本一致

下麵是一些註冊時要依賴的類型

 public interface IHeadphone
 {

 }

 public class Headphone : IHeadphone
 {
     public Headphone(IMicrophone microphone)
     {
         Console.WriteLine("Headphone 被構造");
     }
 }
public interface IMicrophone
{

}

 public class Microphone : IMicrophone
 {

     public Microphone(IPower power)
     {
         Console.WriteLine("Microphone 被構造");
     }
 }
public interface IPower
{

}

public class Power : IPower
{
    public Power(IBLL.IBaseBll baseBll)
    {
        Console.WriteLine("Power 被構造");
    }
}
 public interface IBaseBll
 {
     void DoSomething();
 }
 public class BaseBll : IBaseBll
 {
     private IBaseDAL _baseDAL = null;
     public BaseBll(IBaseDAL baseDAL, int id)
     {
         Console.WriteLine($"{nameof(BaseBll)}被構造。。。{id}。");
         this._baseDAL = baseDAL;
     }
     public void DoSomething()
     {
         this._baseDAL.Add();
         this._baseDAL.Update();
         this._baseDAL.Find();
         this._baseDAL.Delete();
     }
 }

 public interface IBaseDAL
 {
     void Add();
     void Delete();
     void Update();
     void Find();
 }
public class BaseDAL : IBaseDAL
{
    public BaseDAL()
    {
        Console.WriteLine($"{nameof(BaseDAL)}被構造。。。。");
    }

    public void Add()
    {
        Console.WriteLine($"{nameof(Add)}");
    }

    public void Delete()
    {
        Console.WriteLine($"{nameof(Delete)}");
    }

    public void Find()
    {
        Console.WriteLine($"{nameof(Find)}");
    }

    public void Update()
    {
        Console.WriteLine($"{nameof(Update)}");
    }
}
View Code

 

 IUnityContainer container = new UnityContainer();
 container.RegisterType<IPhone, ApplePhone>();
 container.RegisterType<IHeadphone, Headphone>();
 container.RegisterType<IMicrophone, Microphone>();
 container.RegisterType<IPower, Power>();
 container.RegisterType<IBLL.IBaseBll, BLL.BaseBll>();
 container.RegisterType<IDAL.IBaseDAL, Ruamou.DAL.BaseDAL>();
 IPhone iphone = container.Resolve<IPhone>();

但凡是用到了需要的類型,都要給註入進去,不然容器怎麼知道類型啊

 

Unity裡面到底是怎麼實現的?下麵,自己來寫一個IOC

1、最最基礎簡陋的版本:

public interface ILTContainer
    {
        void RegisterType<TFrom, TTo>();
        T Resolve<T>();
    }

 /// <summary>
    /// 容器--工廠
    /// </summary>
    public class LTContainer : ILTContainer
    {
        private Dictionary<string, Type> LTDic = new Dictionary<string, Type>();

        public void RegisterType<TFrom, TTo>()
        {
            LTDic.Add(typeof(TFrom).FullName, typeof(TTo));
        }

        public T Resolve<T>()
        {
            Type type = LTDic[typeof(T).FullName];
            return (T)Activator.CreateInstance(type);
        }
    }
}

調用一下:

ILTContainer container = new LTContainer();
container.RegisterType<IPerson, Student>();

var person = container.Resolve<IPerson>();

 

2、升級一點點

 

 

  public interface IPerson
    {

    }

    public class Student : IPerson
    {
        [LTInjectionConstructor]
        public Student(Animal animal)
        {
            Console.WriteLine("Student被構造了...");
        }

    }

    public abstract class Animal
    {
    }

    public class Cat : Animal
    {
        public Cat()
        {
            Console.WriteLine("Animal被構造了....");
        }
    }
}
  public interface ILTContainer
    {
        void RegisterType<TFrom, TTo>();
        T Resolve<T>();
    }


    /// <summary>
    /// 容器--工廠
    /// </summary>
    public class LTContainer : ILTContainer
    {
        private Dictionary<string, Type> LTDic = new Dictionary<string, Type>();

        public void RegisterType<TFrom, TTo>()
        {
            LTDic.Add(typeof(TFrom).FullName, typeof(TTo));
        }

        public T Resolve<T>()
        {
            Type type = LTDic[typeof(T).FullName];
            var ctorArray = type.GetConstructors();
            ConstructorInfo ctor = null;
            if (ctorArray.Count(c => c.IsDefined(typeof(LTInjectionConstructorAttribute), true)) > 0)
            {
                ctor = ctorArray.FirstOrDefault(c => c.IsDefined(typeof(LTInjectionConstructorAttribute), true));
            }
            else
            {
                ctor = ctorArray.OrderByDescending(c => c.GetParameters().Length).FirstOrDefault();
            }
            List<object> paraList = new List<object>();
            foreach (var item in ctor.GetParameters())
            {
                Type paraType = item.ParameterType;
                Type targetType = this.LTDic[paraType.FullName];
                paraList.Add(Activator.CreateInstance(targetType));
            }
            return (T)Activator.CreateInstance(type, paraList.ToArray());
            //return (T)this.CreateObject(type);

        }

        private object CreateObject(Type type)
        {
            ConstructorInfo[] ctorArray = type.GetConstructors();
            ConstructorInfo ctor = null;
            if (ctorArray.Count(c => c.IsDefined(typeof(LTInjectionConstructorAttribute), true)) > 0)
            {
                ctor = ctorArray.FirstOrDefault(c => c.IsDefined(typeof(LTInjectionConstructorAttribute), true));
            }
            else
            {
                ctor = ctorArray.OrderByDescending(c => c.GetParameters().Length).FirstOrDefault();
            }
            List<object> paraList = new List<object>();
            foreach (var parameter in ctor.GetParameters())
            {
                Type paraType = parameter.ParameterType;
                Type targetType = this.LTDic[paraType.FullName];
                object para = this.CreateObject(targetType);
                //遞歸:隱形的跳出條件,就是GetParameters結果為空,targetType擁有無參數構造函數
                paraList.Add(para);
            }
            return Activator.CreateInstance(type, paraList.ToArray());
        }
        //屬性註入+方法註入?

    }

調用一下:

ILTContainer container = new LTContainer();
 ILTContainer container = new LTContainer();
container.RegisterType<IPerson, Student>();
container.RegisterType<Animal, Cat>();
var person = container.Resolve<IPerson>();

3、再升級一點點:

  繼續找出targetType的構造,找出一個合適的構造函數,分別構造其參數,繼續...遞歸

 

 

 public interface ILTContainer
    {
        void RegisterType<TFrom, TTo>();
        T Resolve<T>();
    }


    /// <summary>
    /// 容器--工廠
    /// </summary>
    public class LTContainer : ILTContainer
    {
        private Dictionary<string, Type> LTDic = new Dictionary<string, Type>();

        public void RegisterType<TFrom, TTo>()
        {
            LTDic.Add(typeof(TFrom).FullName, typeof(TTo));
        }

        public T Resolve<T>()
        {
            Type type = LTDic[typeof(T).FullName];
            //繼續找出targetType的構造函數,找出一個合適的構造函數,分別構造其參數
            //繼續......遞歸
            return (T)this.CreateObject(type);

        }


        public object CreateObject(Type type)
        {
            ConstructorInfo[] ctorArray = type.GetConstructors();
            ConstructorInfo ctor = null;
            if (ctorArray.Count(c => c.IsDefined(typeof(LTContainer), true)) > 0)
            {
                ctor = ctorArray.FirstOrDefault(c => c.IsDefined(typeof(LTContainer), true));
            }
            else
            {
                ctor = ctorArray.OrderByDescending(c => c.GetParameters().Length).FirstOrDefault();
            }
            List<object> paraList = new List<object>();
            foreach (var parameter in ctor.GetParameters())
            {
                Type paraType = parameter.ParameterType;
                Type targetType = this.LTDic[paraType.FullName];
                object para = this.CreateObject(targetType);
                //遞歸:隱形的跳出條件,就是GetParameters結果為空,targetType擁有無參數構造函數
                paraList.Add(para);
            }
            return Activator.CreateInstance(type, paraList.ToArray());
        }
        //屬性註入+方法註入?

    }

 

生命管理周期:

IUnityContainer container = new UnityContainer();

預設瞬時生命周期:每次都是構造一個新的

container.RegisterType<AbstractPad, ApplePad>();
container.RegisterType<AbstractPad, ApplePad>(new TransientLifetimeManager());

全局單例:全局就只有一個該類型實例

非強制性,只有通過容器獲取才是單例;項目中一般推薦容器單例而不是自己寫單例

container.RegisterType<AbstractPad, ApplePad>(new SingletonLifetimeManager());
AbstractPad pad1 = container.Resolve<AbstractPad>();
AbstractPad pad2 = container.Resolve<AbstractPad>();
Console.WriteLine(object.ReferenceEquals(pad1, pad2));

線程單例:同一個線程就只有一個實例,不同線程就是不同實例

container.RegisterType<AbstractPad, ApplePad>(new PerThreadLifetimeManager());
AbstractPad pad1 = null;
AbstractPad pad2 = null;
AbstractPad pad3 = null;

Action act1 = new Action(() =>
                {
                    pad1 = container.Resolve<AbstractPad>();
                    Console.WriteLine($"pad1由線程id={Thread.CurrentThread.ManagedThreadId}");
                });
var result1 = act1.BeginInvoke(null, null);

Action act2 = new Action(() =>
{
    pad2 = container.Resolve<AbstractPad>();
    Console.WriteLine($"pad2由線程id={Thread.CurrentThread.ManagedThreadId}");
});
var result2 = act2.BeginInvoke(t =>
{
    pad3 = container.Resolve<AbstractPad>();
    Console.WriteLine($"pad3由線程id={Thread.CurrentThread.ManagedThreadId}");
    Console.WriteLine($"object.ReferenceEquals(pad2, pad3)={object.ReferenceEquals(pad2, pad3)}");
}, null);

act1.EndInvoke(result1);
act2.EndInvoke(result2);

Console.WriteLine($"object.ReferenceEquals(pad1, pad2)={object.ReferenceEquals(pad1, pad2)}");

//ExternallyControlledLifetimeManager 外部可釋放單例
//PerResolveLifetimeManager 迴圈引用

 

 自己寫的容器裡面,加上生命周期:

 public interface IBingleContainer
 {
     void RegisterType<TFrom, TTo>(LifeTimeType lifeTimeType = LifeTimeType.Transient);
     T Resolve<T>();
 }

 /// <summary>
 /// 容器--工廠
 /// </summary>
 public class BingleContainer : IBingleContainer
 {
     private Dictionary<string, RegisterInfo> BingleContainerDictionary = new Dictionary<string, RegisterInfo>();
     

     /// <summary>
     /// 緩存起來,類型的對象實例
     /// </summary>
     private Dictionary<Type, object> TypeObjectDictionary = new Dictionary<Type, object>();

     /// <summary>
     /// 
     /// </summary>
     /// <typeparam name="TFrom"></typeparam>
     /// <typeparam name="TTo"></typeparam>
     /// <param name="lifeTimeType">預設參數,不傳遞就是Transient</param>
     public void RegisterType<TFrom, TTo>(LifeTimeType lifeTimeType = LifeTimeType.Transient)
     {
         BingleContainerDictionary.Add(typeof(TFrom).FullName, new RegisterInfo()
         {
             TargetType = typeof(TTo),
             LifeTime = lifeTimeType
         });
     }

     public T Resolve<T>()
     {
         RegisterInfo info = BingleContainerDictionary[typeof(T).FullName];
         Type type = BingleContainerDictionary[typeof(T).FullName].TargetType;
         T result = default(T);
         switch (info.LifeTime)
         {
             case LifeTimeType.Transient:
                 result = (T)this.CreateObject(type);
                 break;
             case LifeTimeType.Singleton:
                 if (this.TypeObjectDictionary.ContainsKey(type))
                 {
                     result = (T)this.TypeObjectDictionary[type];
                 }
                 else
                 {
                     result = (T)this.CreateObject(type);
                     this.TypeObjectDictionary[type] = result;
                 }
                 break;
             case LifeTimeType.PerThread:
                 //怎麼保證用線程校驗呢? 線程槽,把數據存在這裡
                 {
                     string key = type.FullName;
                     object oValue = CallContext.GetData(key);
                     if (oValue == null)
                     {
                         result = (T)this.CreateObject(type);
                         CallContext.SetData(key, result);
                     }
                     else
                     {
                         result = (T)oValue;
                     }
                 }
                 break;
             default:
                 throw new Exception("wrong LifeTime");
         }
         return result;
     }
     private object CreateObject(Type type)
     {
         ConstructorInfo[] ctorArray = type.GetConstructors();
         ConstructorInfo ctor = null;
         if (ctorArray.Count(c => c.IsDefined(typeof(BingleInjectionConstructorAttribute), true)) > 0)
         {
             ctor = ctorArray.FirstOrDefault(c => c.IsDefined(typeof(BingleInjectionConstructorAttribute), true));
         }
         else
         {
             ctor = ctorArray.OrderByDescending(c => c.GetParameters().Length).FirstOrDefault();
         }
         List<object> paraList = new List<object>();
         foreach (var parameter in ctor.GetParameters())
         {
             Type paraType = parameter.ParameterType;
             RegisterInfo info = BingleContainerDictionary[paraType.FullName];
             Type targetType = info.TargetType;
             //object para = this.CreateObject(targetType);
             object para = null;
             #region 
                {
                    switch (info.LifeTime)
                    {
                        case LifeTimeType.Transient:
                            para = this.CreateObject(targetType);
                            break;
                        case LifeTimeType.Singleton:
                            //需要線程安全 雙if+lock
                            {
                                if (this.TypeObjectDictionary.ContainsKey(targetType))
                                {
                                    para = this.TypeObjectDictionary[targetType];
                                }
                                else
                                {
                                    para = this.CreateObject(targetType);
                                    this.TypeObjectDictionary[targetType] = para;
                                }
                            }
                            break;
                        case LifeTimeType.PerThread:
                            //怎麼保證用線程校驗呢? 線程槽,把數據存在這裡
                            {
                                string key = targetType.FullName;
                                object oValue = CallContext.GetData(key);
                                if (oValue == null)
                                {
                                    para = this.CreateObject(targetType);
                                    CallContext.SetData(key, para);
                                }
                                else
                                {
                                    para = oValue;
                                }
                            }
                            break;
                        default:
                            throw new Exception("wrong LifeTime");
                    }
                }
                #endregion
             //遞歸:隱形的跳出條件,就是GetParameters結果為空,targetType擁有無參數構造函數
             paraList.Add(para);
         }
         return Activator.CreateInstance(type, paraList.ToArray());
     }
     //屬性註入+方法註入?


 }
public class RegisterInfo
{
    /// <summary>
    /// 目標類型
    /// </summary>
    public Type TargetType { get; set; }
    /// <summary>
    /// 生命周期
    /// </summary>
    public LifeTimeType LifeTime { get; set; }
}

public enum LifeTimeType
{
    Transient,
    Singleton,
    PerThread
}
 IBingleContainer container = new BingleContainer();
 container.RegisterType<IPhone, AndroidPhone>(LifeTimeType.PerThread);
 container.RegisterType<AbstractPad, ApplePad>(LifeTimeType.PerThread);
 container.RegisterType<IHeadphone, Headphone>(LifeTimeType.Transient);
 container.RegisterType<IMicrophone, Microphone>(LifeTimeType.Singleton);
 container.RegisterType<IPower, Power>();
 container.RegisterType<IBLL.IBaseBll, BLL.BaseBll>();
 container.RegisterType<IDAL.IBaseDAL, Ruamou.DAL.BaseDAL>();
 IPhone pad1 = null;
 IPhone pad2 = null;
 IPhone pad3 = null;
 //pad1 = container.Resolve<IPhone>();
 Action act1 = new Action(() =>
 {
     pad1 = container.Resolve<IPhone>();
     Console.WriteLine($"pad1由線程id={Thread.CurrentThread.ManagedThreadId}");
 });
 var result1 = act1.BeginInvoke(null, null);

 Action act2 = new Action(() =>
 {
     pad2 = container.Resolve<IPhone>();
     Console.WriteLine($"pad2由線程id={Thread.CurrentThread.ManagedThreadId}");
 });
 var result2 = act2.BeginInvoke(t =>
 {
     pad3 = container.Resolve<IPhone>();
     Console.WriteLine($"pad3由線程id={Thread.CurrentThread.ManagedThreadId}");
     Console.WriteLine($"object.ReferenceEquals(pad2, pad3)={object.ReferenceEquals(pad2, pad3)}");
 }, null);

 act1.EndInvoke(result1);
 act2.EndInvoke(result2);

 Console.WriteLine($"object.ReferenceEquals(pad1, pad2)={object.ReferenceEquals(pad1, pad2)}");

 

 容器依賴細節?如果不想依賴細節,又想創建對象,反射+配置文件:

 ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
 fileMap.ExeConfigFilename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory + "CfgFiles\\Unity.Config");//找配置文件的路徑
 Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
 UnityConfigurationSection section = (UnityConfigurationSection)configuration.GetSection(UnityConfigurationSection.SectionName);

 IUnityContainer container = new UnityContainer();
 section.Configure(container, "testContainer1");

 // container.AddNewExtension<Interception>().Configure<Interception>()
 //.SetInterceptorFor<IPhone>(new InterfaceInterceptor());

 IPhone phone = container.Resolve<IPhone>();
 phone.Call();
 IPhone android = container.Resolve<IPhone>("Android");
 android.Call();

 IDBContext<Program> context = container.Resolve<IDBContext<Program>>();
 context.DoNothing();

配置文件:

 <unity>
   <!--<sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Microsoft.Practices.Unity.Interception.Configuration"/>-->
   <sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Unity.Interception.Configur

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 因為有時候需要定製化的控制項,需要多個控制項的組合及複雜功能的集成,這樣可以考慮自定義用戶控制項。下麵分享一個簡單的數值增減功能的自定義控制項作為說明。 效果圖如下: 1、創建自定義用戶控制項(添加->新建項->用戶控制項) 2、編寫XAML UI比較簡單,我就不解釋了... 2、編寫後臺代碼 邏輯也比較簡單, ...
  • WPF依賴項屬性可以實現屬性的綁定,成功綁定之後只要修改後臺綁定的屬性,即可UI同步自動更新綁定的值,無需手動刷新界面;同樣,前臺的值變化後,通過獲取綁定的屬性值也可獲取UI變化後的值,實現雙向變化的效果。屬性綁定使得UI更新非常的方便,下麵分享一個小慄子說明使用的方式。 1、先做了一個有一個Tex ...
  • JIT--第一次--標記已--存根--調用--查找存根--執行機器碼 C#和CIL的關係: C#和N#都是CIL實現,但是彼此不能互通: C#和N#公開不分滿足規範,我們才能互通 CLS就是描述多語言互通的規範 記憶體分配:線程棧 堆Heap: 一個程式運行時,該進程存放引用類型變數的一塊記憶體,全局唯 ...
  • 參考地址:https://blog.csdn.net/qiaoquan3/article/details/51380992 1、集合set:純粹的數據集合 2、線性結構:一對一的,數組 3、樹形結構:一對多的,菜單/文件夾/類別/屬性控制項/表達式目錄樹 4、圖形/網狀結構:多對多,地圖應用比較多,網 ...
  • 需求場景 網站a,功能變數名稱為 a.site.com 網站b, 功能變數名稱為 b.site.com 需要在a、b兩個站點之間共用session 解決方案 使用redis作為分散式緩存存儲 設置sessionId cookie 保存的功能變數名稱,使得兩個網站鈞能夠讀取到相同的sessionId 自定義SessionMi ...
  • https://www.cnblogs.com/chenwolong/p/7531955.html EF使用AsNoTracking(),無跟蹤查詢技術(查詢出來的數據不可以修改,如果你做了修改,你會發現修改並不成功) ...
  • 表達式樹練習實踐:入門基礎 [TOC] 什麼是表達式樹 來自微軟官方文檔的定義: 表達式樹以樹形數據結構表示代碼。 它能幹什麼呢? 你可以對錶達式樹中的代碼進行編輯和運算。 這樣能夠動態修改可執行代碼、在不同資料庫中執行 LINQ 查詢以及創建動態查詢。 好不好玩? 表達式樹還能用於動態語言運行時 ...
  • https://www.cnblogs.com/artech/p/net-core-di-01.html 大內老A的在.NET Core下對這些的介紹,有一系列文章 https://www.cnblogs.com/jesse2013/p/di-in-aspnetcore.html https://w ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...