win10重新安裝的系統能否保存應用程序?重新安裝的系統通常無法保存和使用。重新安裝的系統默認格式化了系統磁盤C盤,使用的注冊表文件在磁盤C中,格式化后無法打開原始應用。因此,許多人在重新安裝后無法打開桌面應用程序。
方式:
一般來說,如果在系統的設置中進行升級,您可以選擇在計算機上保存個人文件。當然,這些個人文件只是文檔表和其他數據,而不是應用程序。計算機上的應用程序將被刪除。
通常情況下win10重裝系統將格式化C盤,C磁盤和桌面上的文檔需要自己備份。默認情況下,其他磁盤上的文檔不會受到影響,但為了安全起見,最好對整個文檔進行備份,以防止內容丟失。
對于無法正常進入系統的情況,需要一個u盤制作系統盤,插入自己的電腦,然后重新安裝系統。同樣需要注意的是,u盤文件必須備份,在制作啟動盤時,u盤將被格式化。
當然,您也可以根據您自己的U盤啟動盤備份您自己的系統。如果計算機突然出現問題,您也可以嘗試使用備份U盤恢復系統。許多工具都具有備份恢復系統的功能。您可以根據自己的需要備份系統
在C#中,空引用異常(NullReferenceException)是最常見的異常之一。它發生在你嘗試訪問或操作一個空對象時。換句話說,當你沒有為變量分配一個有效的對象引用,卻嘗試使用它時,就會拋出空引用異常。
您正在嘗試使用空值(或 VB.NET 中的“Nothing”)。這意味著您將其設置為 null,或者根本沒有設置任何值。
與其他任何東西一樣,空值會傳遞。如果在方法“A”中為 null,則可能是方法“B”將 null 傳遞給了方法“A”。
空值可能有不同的含義:
請注意,通過a.Value訪問它如果a為空會引發InvalidOperationException而不是NullReferenceException-您應該事先進行檢查,即如果您有另一個非空變量int b;然后您應該進行賦值,如if(a.HasValue){b=a.Value;}或更短的if(a!=null){b=a;}。
這篇文章的其余部分會更詳細地介紹,并展示許多程序員經常犯的錯誤,這些錯誤可能導致空引用異常。
運行時拋出 NullReferenceException 總是意味著同樣的事情:你試圖使用一個引用,但該引用沒有被初始化(或者曾經被初始化過,但現在已經不再被初始化)。
這意味著該引用是空的,你無法通過空引用訪問成員(比如方法)。這是最簡單的情況:
string foo=null;
foo.ToUpper();
這將在第二行拋出NullReferenceException,因為你不能在指向null的字符串引用上調用實例方法ToUpper()。
你如何找到 NullReferenceException 的源頭?除了查看異常本身會在發生異常的位置拋出之外,Visual Studio 調試的一般規則也適用:設置策略性的斷點并檢查你的變量,可以通過將鼠標懸停在它們的名稱上、打開(快速)監視窗口或者使用諸如本地變量和自動變量等各種調試面板來進行。
如果你想找出引用是在哪里設置或未設置,右鍵單擊它的名稱并選擇“查找所有引用”。然后你可以在每個找到的位置設置斷點,并使用附加了調試器的程序運行。每當調試器在這樣的斷點上中斷時,你需要確定你是否期望引用是非空的,檢查變量,并驗證它在你期望的時候指向一個實例。
通過這種方式跟蹤程序流程,你可以找到實例不應為 null 的位置,以及為什么它沒有被正確設置。
一些常見的異常拋出場景:
ref1.ref2.ref3.member
如果ref1或ref2或ref3為空,那么你會得到一個NullReferenceException。如果你想解決這個問題,那么找出哪一個是空的,通過將表達式重寫為更簡單的等價形式來解決。
var r1=ref1;
var r2=r1.ref2;
var r3=r2.ref3;
r3.member
在HttpContext.Current.User.Identity.Name中,HttpContext.Current可能為null,或者User屬性可能為null,或者Identity屬性可能為null。
public class Person
{
public int Age { get; set; }
}
public class Book
{
public Person Author { get; set; }
}
public class Example
{
public void Foo()
{
Book b1=new Book();
int authorAge=b1.Author.Age; // You never initialized the Author property.
// there is no Person to get an Age from.
}
}
如果你想避免子對象(People)的空引用,你可以在父對象(BooK)的構造函數中對其進行初始化。
同樣的規則也適用于嵌套對象初始化器:
Book b1=new Book
{
Author={ Age=45 }
};
轉換為
Book b1=new Book();
b1.Author.Age=45;
雖然使用了新關鍵字,它只創建了 Book 的一個新實例,而不是 Person 的新實例,所以 Author 屬性仍然為空。
public class Person
{
public ICollection<Book> Books { get; set; }
}
public class Book
{
public string Title { get; set; }
}
嵌套集合初始化器的行為相同:
Person p1=new Person
{
Books={
new Book { Title="Title1" },
new Book { Title="Title2" },
}
};
轉換為
Person p1=new Person();
p1.Books.Add(new Book { Title="Title1" });
p1.Books.Add(new Book { Title="Title2" });
使用 new Person 只會創建一個 Person 的實例,但 Books 集合仍然為空。集合初始化器語法不會為 p1.Books 創建一個集合,它只是將其轉換為 p1.Books.Add(...) 語句。
int[] numbers=null;
int n=numbers[0]; // numbers is null. There is no array to index.
Person[] people=new Person[5];
people[0].Age=20 // people[0] is null. The array was allocated but not
// initialized. There is no Person to set the Age for.
long[][] array=new long[1][];
array[0][0]=3; // is null because only the first dimension is yet initialized.
// Use array[0]=new long[2]; first.
Dictionary<string, int> agesForNames=null;
int age=agesForNames["Bob"]; // agesForNames is null.
// There is no Dictionary to perform the lookup.
public class Person
{
public string Name { get; set; }
}
var people=new List<Person>();
people.Add(null);
var names=from p in people select p.Name;
string firstName=names.First(); // Exception is thrown here, but actually occurs
// on the line above. "p" is null because the
// first element we added to the list is null.
public class Demo
{
public event EventHandler StateChanged;
protected virtual void OnStateChanged(EventArgs e)
{
StateChanged(this, e); // Exception is thrown here
// if no event handlers have been attached
// to StateChanged event
}
}
(注意:VB.NET 編譯器會在事件使用時插入空值檢查,因此在 VB.NET 中不需要檢查事件是否為 Nothing。)
如果你將字段命名與局部變量不同,你可能會意識到你從未初始化該字段。
public class Form1
{
private Customer customer;
private void Form1_Load(object sender, EventArgs e)
{
Customer customer=new Customer();
customer.Name="John";
}
private void Button_Click(object sender, EventArgs e)
{
MessageBox.Show(customer.Name);
}
}
這個問題可以通過遵循在字段前加下劃線的命名約定來解決:
private Customer _customer;
public partial class Issues_Edit : System.Web.UI.Page
{
protected TestIssue myIssue;
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
// Only called on first load, not when button clicked
myIssue=new TestIssue();
}
}
protected void SaveButton_Click(object sender, EventArgs e)
{
myIssue.Entry="NullReferenceException here!";
}
}
// if the "FirstName" session value has not yet been set,
// then this line will throw a NullReferenceException
string firstName=Session["FirstName"].ToString();
如果在 ASP.NET MVC 視圖中引用 @Model 的屬性時發生異常,你需要明白 Model 是在你的操作方法中設置的,當你返回一個視圖時。當你從控制器返回一個空模型(或模型屬性)時,視圖在訪問它時會發生異常。
// Controller
public class Restaurant:Controller
{
public ActionResult Search()
{
return View(); // Forgot the provide a Model here.
}
}
// Razor view
@foreach (var restaurantSearch in Model.RestaurantSearch) // Throws.
{
}
<p>@Model.somePropertyName</p> <!-- Also throws -->
WPF 控件在調用 InitializeComponent 時按照它們在可視樹中出現的順序創建。如果在 InitializeComponent 中引用了在后續創建階段的控件的早期創建控件中的事件處理程序等,就會引發 NullReferenceException。
例如:
<Grid>
<!-- Combobox declared first -->
<ComboBox Name="comboBox1"
Margin="10"
SelectedIndex="0"
SelectionChanged="comboBox1_SelectionChanged">
<ComboBoxItem Content="Item 1" />
<ComboBoxItem Content="Item 2" />
<ComboBoxItem Content="Item 3" />
</ComboBox>
<!-- Label declared later -->
<Label Name="label1"
Content="Label"
Margin="10" />
</Grid>
在這里,comboBox1 在 label1 之前創建。如果 comboBox1_SelectionChanged 嘗試引用 `label1,它可能尚未被創建。
private void comboBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
label1.Content=comboBox1.SelectedIndex.ToString(); // NullReferenceException here!!
}
改變 XAML 中聲明的順序(即,在 comboBox1 之前列出 label1,忽略設計哲學的問題)至少會解決這里的 NullReferenceException。
var myThing=someObject as Thing;
這種方法在類型轉換失敗時不會拋出 InvalidCastException,而是返回 null(當 someObject 本身為 null 時也是如此)。所以請注意這一點。
普通的 First() 和 Single() 方法在沒有匹配項時會拋出異常。而帶有 "OrDefault" 后綴的版本會返回 null。所以請注意這一點。
當嘗試對空集合進行迭代時,foreach 會拋出異常。這通常是由返回空集合的方法意外返回 null 導致的。
List<int> list=null;
foreach(var v in list) { } // NullReferenceException here
更現實的例子是從 XML 文檔中選擇節點。如果未找到節點,會拋出異常,但初始調試顯示所有屬性都是有效的。
foreach (var node in myData.MyXml.DocumentNode.SelectNodes("//Data"))
如果你期望引用有時會是 null,可以在訪問實例成員之前檢查它是否為 null:
void PrintName(Person p)
{
if (p !=null)
{
Console.WriteLine(p.Name);
}
}
你調用的方法可能返回 null,例如當尋找的對象找不到時。在這種情況下,你可以選擇返回一個默認值:
string GetCategory(Book b)
{
if (b==null)
return "Unknown";
return b.Category;
}
你還可以拋出自定義異常,然后在調用代碼中捕獲它:
string GetCategory(string bookTitle)
{
var book=library.FindBook(bookTitle); // This may return null
if (book==null)
throw new BookNotFoundException(bookTitle); // Your custom exception
return book.Category;
}
當你在開發過程中知道一個方法可能會返回 null,但實際上不應該返回 null 時,你可以使用 Debug.Assert(),以便在出現這種情況時盡快中斷程序。
string GetTitle(int knownBookID)
{
// You know this should never return null.
var book=library.GetBook(knownBookID);
// Exception will occur on the next line instead of at the end of this method.
Debug.Assert(book !=null, "Library didn't return a book for known book ID.");
// Some other code
return book.Title; // Will never throw NullReferenceException in Debug mode.
}
盡管這個檢查不會出現在發布版本中,但在運行時(在發布模式下)當 `book==null` 時,它會再次引發 NullReferenceException。
DateTime? appointment=null;
Console.WriteLine(appointment.GetValueOrDefault(DateTime.Now));
// Will display the default value provided (DateTime.Now), because appointment is null.
appointment=new DateTime(2022, 10, 20);
Console.WriteLine(appointment.GetValueOrDefault(DateTime.Now));
// Will display the appointment date, not the default
遇到 null 時提供默認值的簡寫方式是使用空合并操作符(`??`)。
IService CreateService(ILogger log, Int32? frobPowerLevel)
{
var serviceImpl=new MyService(log ?? NullLog.Instance);
// Note that the above "GetValueOrDefault()" can also be rewritten to use
// the coalesce operator:
serviceImpl.FrobPowerLevel=frobPowerLevel ?? 5;
}
有時這也被稱為安全導航或Elvis(根據其形狀)運算符。如果運算符左側的表達式為空,那么右側將不會被計算,而是返回空值。這意味著像這樣的情況:
var title=person.Title.ToUpper();
如果person沒有title,這將拋出一個異常,因為它試圖在一個空值屬性上調用 ToUpper。
在C# 5及以下版本中,可以通過以下方式加以防范:
var title=person.Title==null ? null : person.Title.ToUpper();
現在,title變量將為空,而不會拋出異常。C# 6引入了一個更簡潔的語法來實現這一點:
var title=person.Title?.ToUpper();
這將導致title變量為空,如果person.Title為空,就不會調用ToUpper。
當然,你仍然需要檢查title是否為空,或者使用空值條件運算符與空值合并運算符(??)一起使用,以提供一個默認值:
// regular null check
int titleLength=0;
if (title !=null)
titleLength=title.Length; // If title is null, this would throw NullReferenceException
// combining the `?` and the `??` operator
int titleLength=title?.Length ?? 0;
同樣,對于數組,你可以使用?[i]來實現如下功能:
int[] myIntArray=null;
var i=5;
int? elem=myIntArray?[i];
if (!elem.HasValue) Console.WriteLine("No value");
這將實現以下功能:如果myIntArray為空,表達式將返回null,你可以安全地進行檢查。如果它包含一個數組,它將執行與 elem=myIntArray[i]; 相同的操作,并返回第i個元素。
在C# 8中引入了空值上下文和可空引用類型,它們對變量進行靜態分析,并在值可能為空或已設置為null時提供編譯器警告。可空引用類型允許明確允許類型為空。
可以使用csproj文件中的Nullable元素為項目設置可空注解上下文和可空警告上下文。此元素配置編譯器如何解釋類型的可空性以及生成哪些警告。有效的設置包括:
可空引用類型的表示與可空值類型相同:在變量類型后追加?。
C#支持“迭代器塊”(在其他一些流行的語言中稱為“生成器”)。NullReferenceException在迭代器塊中可能特別難以調試,因為它具有延遲執行的特性。
public IEnumerable<Frob> GetFrobs(FrobFactory f, int count)
{
for (int i=0; i < count; ++i)
yield return f.MakeFrob();
}
...
FrobFactory factory=whatever;
IEnumerable<Frobs> frobs=GetFrobs();
...
foreach(Frob frob in frobs) { ... }
如果任何結果為空,那么MakeFrob就會拋出異常。現在,你可能會認為正確的做法是這樣:
// DON'T DO THIS
public IEnumerable<Frob> GetFrobs(FrobFactory f, int count)
{
if (f==null)
throw new ArgumentNullException("f", "factory must not be null");
for (int i=0; i < count; ++i)
yield return f.MakeFrob();
}
為什么這是錯誤的?因為迭代器塊實際上直到foreach才運行!對GetFrobs的調用只是返回一個對象,當迭代時才運行迭代器塊。
通過編寫這樣的空值檢查,您可以避免NullReferenceException,但是將NullArgumentException移動到迭代點而不是調用點非常令人困惑。
正確的修復方法是:
// DO THIS
public IEnumerable<Frob> GetFrobs(FrobFactory f, int count)
{
// No yields in a public method that throws!
if (f==null)
throw new ArgumentNullException("f", "factory must not be null");
return GetFrobsForReal(f, count);
}
private IEnumerable<Frob> GetFrobsForReal(FrobFactory f, int count)
{
// Yields in a private method
Debug.Assert(f !=null);
for (int i=0; i < count; ++i)
yield return f.MakeFrob();
}
這樣,創建一個私有的輔助方法,其中包含迭代器塊邏輯,以及一個公共的表面方法,用于進行空值檢查并返回迭代器。現在,當調用GetFrobs時,空值檢查會立即發生,然后在迭代序列時執行GetFrobsForReal。
如果你檢查LINQ to Objects的參考源代碼,你會發現這種技術被廣泛使用。這樣寫起來可能有點笨拙,但可以更輕松地調試空值錯誤。優化你的代碼以方便調用者,而不是以方便作者為首要考慮。
C#有一個“不安全”模式,顧名思義,非常危險,因為不強制執行提供內存安全性和類型安全性的正常安全機制。除非您深入了解內存工作原理,否則不應編寫不安全的代碼。
在不安全模式下,您應該注意兩個重要事實:
解除引用空指針會產生與解除引用空引用相同的異常
在某些情況下,解除引用無效的非空指針也可能產生該異常
要理解其中的原因,了解.NET如何首先生成NullReferenceException會有所幫助。(這些細節適用于在Windows上運行的.NET;其他操作系統使用類似的機制。)
在Windows中,內存是虛擬化的;每個進程都獲得許多“頁面”的虛擬內存空間,這些頁面由操作系統跟蹤。每個內存頁面都有設置的標志,確定它如何使用:讀取、寫入、執行等。最低頁面標記為“如果以任何方式使用,則產生錯誤”。
在C#中,空指針和空引用都被內部表示為數字零,因此任何嘗試將其解除引用為其對應的內存存儲都會導致操作系統產生錯誤。然后,.NET運行時檢測到此錯誤并將其轉換為NullReferenceException。
這就是為什么解除引用空指針和空引用都會產生相同異常的原因。
第二點呢?解除引用任何無效指針,該指針位于虛擬內存的最低頁面中,會導致相同的操作系統錯誤,從而導致相同的異常。
為什么這有意義呢?假設我們有一個包含兩個int和一個等于null的非托管指針的結構體。如果我們嘗試解除引用結構體中的第二個int,則CLR將不會嘗試訪問位置零處的存儲;它將訪問位置四處的存儲。但從邏輯上講,這是一個空解除引用,因為我們通過null到達了該地址。
如果您正在使用不安全的代碼并且遇到NullReferenceException,請注意有問題的指針不一定為空。它可以是最低頁面中的任何位置,并且將產生此異常。