Android之Realm詳解

来源:https://www.cnblogs.com/WUXIAOCHANG/archive/2019/04/06/10662998.html
-Advertisement-
Play Games

文章大綱 一、Realm介紹二、Realm實戰三、Realm官方文檔四、項目源碼下載五、參考文章 一、Realm介紹 1. 什麼是Realm Realm 是一個手機資料庫,是用來替代 SQlite 的解決方案,比 SQlite 更輕量級,速度更快,因為它有一套自己的資料庫搜索引擎,並且還具有很多現代 ...


文章大綱

一、Realm介紹
二、Realm實戰
三、Realm官方文檔
四、項目源碼下載
五、參考文章

 

一、Realm介紹

1. 什麼是Realm

  Realm 是一個手機資料庫,是用來替代 SQlite 的解決方案,比 SQlite 更輕量級,速度更快,因為它有一套自己的資料庫搜索引擎,並且還具有很多現代資料庫的優點,支持 JSON,流式 API 調用,數據變更通知,自動數據同步,簡單身份驗證,訪問控制,事件處理,最重要的是跨平臺,目前已經支持 Java、Swift、Object - C、React - Native 等多種實現。

2. Realm優勢

易用
  Ream 不是在SQLite基礎上的ORM,它有自己的數據查詢引擎。並且十分容易使用。
快速
  由於它是完全重新開始開發的資料庫實現,所以它比任何的ORM速度都快很多,甚至比SLite速度都要快。
跨平臺
  Realm 支持 iOS & OS X (Objective‑C & Swift) & Android。我們可以在這些平臺上共用Realm資料庫文件,並且上層邏輯可以不用任何改動的情況下實現移植。
高級
  Ream支持加密,格式化查詢,易於移植,支持JSON,流式api,數據變更通知等高級特性
可視化
  Realm 還提供了一個輕量級的資料庫查看工具,在Mac Appstore 可以下載“Realm Browser”這個工具,開發者可以查看資料庫當中的內容,執行簡單的插入和刪除數據的操作。(windows上還不清楚)

3. 使用要求

(1)目前不支持Android以外的Java
(2)Android Studio >= 1.5.1(不支持Eclipse)
(3)JDK version >=7.
(4)支持API 9(Android 2.3)以及之後的版本

二、Realm實戰

1. 添加依賴

在project的build中加入依賴

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath "io.realm:realm-gradle-plugin:2.2.1"
    }
}
  image

在module中加入

apply plugin: 'realm-android'
 

2. 創建model

  創建一個User類,需要繼承RealmObject。支持public, protected和 private的類以及方法

public class User extends RealmObject {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

  除了直接繼承於RealmObject來聲明 Realm 數據模型之外,還可以通過實現 RealmModel介面並添加 @RealmClass修飾符來聲明。

@RealmClass
public class User implements RealmModel {
    ...
}

支持的屬性
boolean, byte, short,int,long,float, double,String, Date 和,byte[], RealmObject, RealmList<? extends RealmObject>
還支持Boolean, Byte, Short, Integer, Long, Float 和 Double
提示:整數類型 short、int 和 long 都被映射到 Realm 內的相同類型(實際上為 long )
(1)@PrimaryKey——表示該欄位是主鍵
  使用過資料庫的同學應該看出來了,PrimaryKey就是主鍵。使用@PrimaryKey來標註,欄位類型必須是字元串(String)或整數(byte,short,int或long)以及它們的包裝類型(Byte,Short, Integer, 或 Long)。不可以存在多個主鍵,使用字元串欄位作為主鍵意味著欄位被索引(註釋@PrimaryKey隱式地設置註釋@Index)。

@PrimaryKey
private String id;

(2)@Required——表示該欄位非空
  在某些情況下,有一些屬性是不能為null的。使用@Required可用於用於強行要求其屬性不能為空,只能用於Boolean, Byte, Short, Integer, Long, Float, Double, String, byte[] 和 Date。在其它類型屬性上使用。

(3)@Required修飾會導致編譯失敗。
  提示:基本數據類型不需要使用註解 @Required,因為他們本身就不可為空。

@Required
private String name;

(4)@Ignore——表示忽略該欄位
  被添加@Ignore標簽後,存儲數據時會忽略該欄位。

@Ignore
private String name;

(5)@Index——添加搜索索引
  為欄位添加搜索索引,這樣會使得插入的速度變慢,數據量也變得更大。不過在查詢速度將變得更快,建議只在優化讀取性能的特定情況時添加索引。支持索引:String,byte,short,int,long,boolean和Date欄位。

註意:如果你創建Model並運行過,然後修改了Model。那麼就需要升級資料庫,否則會拋異常。升級方式後面會提到

3. 初始化Realm

重寫Application類

public class MyApp extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
}
}

AndroidManifest.xml中配置MyApp

 

創建方式1

public class MyApp extends Application {
    @Override
    public void onCreate() {
        super.onCreate();

        Realm.init(this);//初始化realm

        Realm mRealm = Realm.getDefaultInstance();
    }
}

  這時候會創建一個叫做 default.realm的Realm文件,一般來說,這個文件位於/data/data/包名/files/。通過realm.getPath()來獲得該Realm的絕對路徑。

創建方式2

public class MyApp extends Application {
    @Override
    public void onCreate() {
        super.onCreate();

        Realm.init(this);//初始化realm

        //這時候會創建一個叫做 default.realm的Realm文件,一般來說,
        // 這個文件位於/data/data/包名/files/。通過realm.getPath()來獲得該Realm的絕對路徑
        RealmConfiguration config = new RealmConfiguration.Builder()
                .name("myrealm.realm") //文件名
                .schemaVersion(0) //版本號
                .build();
        Realm realm = Realm.getInstance(config);

    }

創建方式3

public class MyApp extends Application {
    @Override
    public void onCreate() {
        super.onCreate();

        Realm.init(this);//初始化realm


        //創建保存在記憶體中的realm,應用關閉後就清除了
//        RealmConfiguration myConfig = new RealmConfiguration.Builder(this)
//                .name("myrealm.realm")
//                .inMemory() .build();

    }

創建方式4

public class MyApp extends Application {
    @Override
    public void onCreate() {
        super.onCreate();

        Realm.init(this);//初始化realm

        
        //創建加密的realm
//        byte[] key = new byte[64];
//        new SecureRandom().nextBytes(key);
//        RealmConfiguration config = new RealmConfiguration.Builder()
//                .encryptionKey(key)
//                .build();
//        Realm realm = Realm.getInstance(config);

    }
}

總結:
RealmConfiguration支持的方法:
Builder.name : 指定資料庫的名稱。如不指定預設名為default。
Builder.schemaVersion : 指定資料庫的版本號。
Builder.encryptionKey : 指定資料庫的密鑰。
Builder.migration : 指定遷移操作的遷移類。
Builder.deleteRealmIfMigrationNeeded : 聲明版本衝突時自動刪除原資料庫。
Builder.inMemory : 聲明資料庫只在記憶體中持久化。
build : 完成配置構建。

4. 關閉Realm

記得使用完後,在onDestroy中關閉Realm

@Override 
protected void onDestroy() { 
    super.onDestroy();
    // Close the Realm instance. 
    realm.close(); 
}

5. 獲取realm對象

public class RealmHelper {
    public static final String DB_NAME = "myRealm.realm";
    private Realm mRealm;


    public RealmHelper(Context context) {

        mRealm = Realm.getDefaultInstance();
    }

public Realm getRealm(){

        return mRealm;
    }

6. 增加數據

  寫入操作需要在事務中進行,可以使用executeTransaction方法來開啟事務。
(1)使用executeTransaction方法插入數據

mRealm.executeTransaction(new Realm.Transaction() {
            @Override
            public void execute(Realm realm) {
                User user = realm.createObject(User.class);
                user.setName("Gavin");
                user.setAge(23);
            }
        });

  註意:如果在UI線程中插入過多的數據,可能會導致主線程擁塞。

(2)使用copyToRealmOrUpdate或copyToRealm方法插入數據
當Model中存在主鍵的時候,推薦使用copyToRealmOrUpdate方法插入數據。如果對象存在,就更新該對象;反之,它會創建一個新的對象。若該Model沒有主鍵,使用copyToRealm方法,否則將拋出異常。

 final User user = new User();
        user.setName("Jack");
        user.setId("2");
        mRealm.executeTransaction(new Realm.Transaction() {
            @Override
            public void execute(Realm realm) {
                realm.copyToRealmOrUpdate(user);
            }
        });

如果你用的是這樣的modle

public class User2 extends RealmObject {
      public String name;
      public int age;
}

就這樣寫

mRealm.executeTransaction(new Realm.Transaction() {
            @Override
            public void execute(Realm realm) {
                User2 user = realm.createObject(User2.class);
                user.name = "Micheal";
                user.age = 30;
            }
        });

(3)上面都是用可executeTransaction方法插入數據,還有另一種方法可以用於插入數據——beginTransaction和commitTransaction

mRealm.beginTransaction();//開啟事務
User user = mRealm.createObject(User.class);
user.setName("Gavin");
user.setId("3");
mRealm.commitTransaction();//提交事務

  在插入前,先調用beginTransaction(),完成後調用commitTransaction()即可。
  註意:在UI和後臺線程同時開啟創建write的事務,可能會導致ANR錯誤。為了避免該問題,可以使用executeTransactionAsync來實現。

(4)使用executeTransactionAsync
該方法會開啟一個子線程來執行事務,並且在執行完成後進行結果通知。

RealmAsyncTask transaction = mRealm.executeTransactionAsync(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        User user = realm.createObject(User.class);
        user.setName("Eric");
        user.setId("4");
      }
});

還可以加入監聽

RealmAsyncTask transaction =  mRealm.executeTransactionAsync(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        User user = realm.createObject(User.class);
        user.setName("Eric");
        user.setId("4");
      }
}, new Realm.Transaction.OnSuccess() {
    @Override
    public void onSuccess() {
        //成功回調
      }
}, new Realm.Transaction.OnError() {
    @Override
    public void onError(Throwable error) {
        //失敗回調
      }
});

  註意:如果當Acitivity或Fragment被銷毀時,在OnSuccess或OnError中執行UI操作,將導致程式奔潰 。用RealmAsyncTask .cancel();可以取消事務在onStop中調用,避免crash

public void onStop () {
    if (transaction != null && !transaction.isCancelled()) {
        transaction.cancel();
      }
}

(5)Realm還是個很好的功能就是將Json字元串轉化為對象

// 一個city model
public class City extends RealmObject {
    private String city;
    private int id;
    // getters and setters left out ...
}
// 使用Json字元串插入數據
realm.executeTransaction(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        realm.createObjectFromJson(City.class, "{ city: \"Copenhagen\", id: 1 }");
    }
});
// 使用InputStream插入數據
realm.executeTransaction(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        try {
            InputStream is = new FileInputStream(new File("path_to_file"));
            realm.createAllFromJson(City.class, is);
        } catch (IOException e) {
            throw new RuntimeException();
        }
    }
});

Realm 解析 JSON 時遵循如下規則:
使用包含空值(null)的 JSON 創建對象:
對於非必須(可為空值的屬性),設置其值為 null;
對於必須(不可為空值的屬性),拋出異常;
使用包含空值(null)的 JSON 更新對象:
對於非必須(可為空值的屬性),設置其值為 null;
對於必須(不可為空值的屬性),拋出異常;
使用不包含對應屬性的 JSON: * 該屬性保持不變

7. 查詢數據

查找操作就比插入方便多了,並不需在事務中操作,直接查詢即可。
(1)findAll ——查詢
例:查詢所有的User

RealmResults<User> userList = mRealm.where(User.class).findAll();

(2)findAllAsync——非同步查詢
當數據量較大,可能會引起ANR的時候,就可以使用findAllAsync

RealmResults<User> userList = mRealm.where(User.class)
                .equalTo("name", "Gavin")
                .findAllAsync();

(3)findFirst ——查詢第一條數據
例:查詢User表中的第一條數據

User user2 = mRealm.where(User.class).findFirst();

(4)equalTo ——根據條件查詢
例:得到name為Gavin的用戶列表。

RealmResults<User> userList = mRealm.where(User.class)
            .equalTo("name", "Gavin").findAll();

如果User中還有Dog屬性,希望根據Dog的條件來獲取用戶:
例:查詢dogs.name為二哈的User

RealmResults<User> userList = mRealm.where(User.class)
             .equalTo("dogs.name", "二哈").findAll();

  得到有養有dogs.name為”二哈”的用戶列表(這裡的dogs是User表中的屬性名)

(5)equalTo ——多條件查詢
當然,我們還經常要用到多條件的查詢的功能。
例:找到用戶名為“Gavin”,且dogs.name為“二哈”的User

RealmResults<User> userList = mRealm.where(User.class)
            .equalTo("name", "Gavin").findAll();
RealmResults<User> userList = user5.where()
            .equalTo("dogs.name", "二哈").findAll();

(6)排序
查詢結束後,還可以進行排序,就像這樣:

RealmResults<User> userList = mRealm.where(User.class) .findAll();
userList = result.sort("age"); //根據age,正序排列
userList = result.sort("age", Sort.DESCENDING);//逆序排列

(7)聚合
RealmResult自帶一些聚合方法:

RealmResults<User> results = realm.where(User.class).findAll();
long   sum     = results.sum("age").longValue();
long   min     = results.min("age").longValue();
long   max     = results.max("age").longValue();
double average = results.average("age");
long   matches = results.size();

(8)更多查詢條件
  上面就展示了equalTo的用法。當然,查詢還有更多的用法,我就不一一示例了。已知的方法如下:
sum():對指定欄位求和。
average():對指定欄位求平均值。
min(): 對指定欄位求最小值。
max() : 對指定欄位求最大值。count : 求結果集的記錄數量。
findAll(): 返回結果集所有欄位,返回值為RealmResults隊列
findAllSorted() : 排序返回結果集所有欄位,返回值為RealmResults隊列
between(), greaterThan(),lessThan(), greaterThanOrEqualTo() & lessThanOrEqualTo()
equalTo() & notEqualTo()
contains(), beginsWith() & endsWith()
isNull() & isNotNull()
isEmpty()& isNotEmpty()

8. 修改數據

mRealm.executeTransaction(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        //先查找後得到User對象
        User user = mRealm.where(User.class).findFirst();
        user.setAge(26);
    }
});

修改也是需要在事務中操作。使用查詢語句得到數據,然後將內容改了即可。

9. 刪除數據

(1)使用deleteFromRealm()

//先查找到數據
final RealmResults<User> userList = mRealm.where(User.class).findAll();
mRealm.executeTransaction(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        userList.get(0).deleteFromRealm();
    }
});

(2)使用deleteFromRealm(int index)

mRealm.executeTransaction(new Realm.Transaction() {
    @Override
    public void execute(Realm realm) {
        userList.deleteFromRealm(0);
    }
});

(3)其他方法

userList.deleteFirstFromRealm(); //刪除user表的第一條數據
userList.deleteLastFromRealm();//刪除user表的最後一條數據
results.deleteAllFromRealm();//刪除user表的全部數據

10. 非同步任務註意點

最後在銷毀Activity或Fragment時,要取消掉非同步任務

@Override
    protected void onDestroy() {
        super.onDestroy();
       if (deleteTask!=null&&!addTask.isCancelled()){
            deleteTask.cancel();
        }
    }

11. 其他常見操作

API:RealmObjectSchema
(1)取消id必填

personSchema.setNullable("id", true):

(2)移除id欄位

personSchema.removeField("id");

(3)重命名

personSchema..renameField("id", "userId");

API:DynamicRealmObject
(4)獲取id

String id = obj.getString("id");

(5)為欄位設置值

obj.setString("name", "Gavin");
obj.setInt("id", 1);
obj.setLong("id", 1);

12. 資料庫版本升級

  當數據結構發生變化是,需要升級資料庫。對於Realm來說,資料庫升級就是遷移操作,把原來的資料庫遷移到新結構的資料庫

  例1:User類發生變化,移除age,新增個@Required的id欄位。
User版本:version 0

String name;
int    age;

User版本:version 1

@Required
String    id;
String name;

創建遷移類CustomMigration,需要實現RealmMigration介面。執行版本升級時的處理:

 /**
     * 升級資料庫
     */
    class CustomMigration implements RealmMigration {
        @Override
        public void migrate(DynamicRealm realm, long oldVersion, long newVersion) {
            RealmSchema schema = realm.getSchema();
            if (oldVersion == 0 && newVersion == 1) {
                RealmObjectSchema personSchema = schema.get("User");
                //新增@Required的id
                personSchema
                        .addField("id", String.class, FieldAttribute.REQUIRED)
                        .transform(new RealmObjectSchema.Function() {
                            @Override
                            public void apply(DynamicReal
mObject obj) {
                                obj.set("id", "1");//為id設置值
                            }
                        })
                        .removeField("age");//移除age屬性
                oldVersion++;
            }
        }
    }

使用Builder.migration升級資料庫,將版本號改為1(原版本號:0)。當Realm發現新舊版本號不一致時,會自動使用該遷移類完成遷移操作。

RealmConfiguration config = new RealmConfiguration.Builder() 
            .name("myrealm.realm") //文件名
            .schemaVersion(1) 
            .migration(new CustomMigration())//升級資料庫
            .build();

例2:加入Dog類,User中加入Dog集合。
User版本:version 1

@Required
String    id;
String name;

User版本:version 2

@Required
private String id;
private String name;
private RealmList<Dog> dogs;

Dog類

public class Dog extends RealmObject {
        private String name;
        private int age;
}

在遷移類CustomMigration中,繼續添加處理方法。

(oldVersion == 1 && newVersion == 2) {
                //創建Dog表
                RealmObjectSchema dogSchema = schema.create("Dog");
                dogSchema.addField("name", String.class);
                dogSchema.addField("age", int.class);

                //User中添加dogs屬性
                schema.get("User")
                        .addRealmListField("dogs", dogSchema)
                        .transform(new RealmObjectSchema.Function() {
                            @Override
                            public void apply(DynamicRealmObject obj) {
                                //為已存在的數據設置dogs數據
                                DynamicRealmObject dog = realm.createObject("Dog");
                                dog.set("name", "二哈");
                                dog.set("age", 2);
                                obj.getList("dogs").add(dog);
                            }
                        });
                oldVersion++;
            }

三、Realm官方文檔

https://realm.io/cn/docs/java/latest/

四、項目源碼下載

鏈接:https://pan.baidu.com/s/1wSoXuclZ-Nwntnw_79xkLA
密碼:oc95

五、參考文章

    1. https://blog.csdn.net/u013651026/article/details/79481615
    2. https://www.jianshu.com/p/28912c2f31db

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

-Advertisement-
Play Games
更多相關文章
  • 單例模式簡介 單例模式是GOF 23個設計模式中最簡單的模式了,它提供了一種創建唯一對象的最佳實現,註意此處的簡單隻是表述和意圖很簡單,但是實現起來,尤其是實現一個優美的單例模式卻沒有那麼簡單。 單例模式歸根結底就是要確保一個類只有一個實例,並提供一個全局方式來訪問該實例。具體而言,這種模式涉及到一 ...
  • linux 搭建squid代理伺服器 實驗環境: 一臺linux搭建Web伺服器,充當內網web伺服器(同時充當內網客戶端) 202.100.10.100 一臺linux系統充當網關伺服器,兩個網卡,開啟路由轉發 192.168.133.131和202.100.10.1 一臺linux搭建Web服務 ...
  • 在《Linux設備驅動程式》一書中讀到的內核模塊編譯Makefile,不是非常理解,在查詢很多資料後,在這裡做個總結。 書中Makefile代碼: 代碼解析: 1. 判斷變數KERNELRELEASE是否設置,該變數在linux內核頂層Makefile中會被設置。當然第一次執行makefile時,K ...
  • 1.下載npm軟體包 點擊鏈接進入下載頁面:npm下載 2.下載完成後將壓縮包放到家目錄下就可以(也可以放到其他地方) 3.解壓 tar -zxvf 壓縮包名稱,解壓後你會得到一個文件夾,進入後是這樣的。 4.然後我們進入scripts這個目錄。 可以看到這裡有一個文件的名稱叫install.sh. ...
  • 起因: 串口IAP升級在正點原子的常式中有講解,正點原子的方法是:在RAM中開闢一個120K的數據空間,用來存放bin文件,bin文件通過串口一次性發送到單片機,然後再實現程式的跳轉。但是這種方法在實際項目中並不實用,因為沒用文件校驗,不能保證bin文件的完整性,如果貿然跳轉,將會是設備陷入到永遠無 ...
  • redis設計關係資料庫 [toc] 前言 最近需要一張用戶信息表,因為數據量並不大,想先放在記憶體中,等需求變更了,再移到磁碟上,或者往mysql塞,那麼問題來了,怎麼用redis的數據類型設計一個關係資料庫呢。 redis只有key value這種存儲結構,如果想利用它做成想其他資料庫一樣具備 等 ...
  • 理解Cursor對象和查詢運算符 cursor對象 cursor對象相當於一個指針,可通過迭代它來訪問MongdoDB資料庫中的一組對象。 在使用 find() 方法查詢時,返回的並非實際文檔,而是一個Cursor對象,也就是一個指向第一個數據之前的指針。 Cursor對象內部存儲了一個指向當前位置 ...
  • 在redis.c的initServerConfig()方法中,通過調用dictCreate方法初始化server端的命令表。這個命令表是一個hashtable,可以通過key找到相關的命令: initServer()函數在初始化服務端的基本配置時,已經提前創建了客戶端的回調函數。具體的調用為:1.創 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...