析构函数(管理C#)

1. 前言

WPF的本地化是个很宽泛的功力,我做过的WPF程序大部分都实现了本地化(不管最终有没有应用)。平时本地化有以下几点需求:

  • 在先后启动时遵照CultureInfo.CurrentUICulture或部署项显示对应语言的UI。
  • 在程序运行时方可动态切换UI语言(无需重启程序)。
  • 制作对应不同语言的安装包。
  • 由此下载语言包实现多种语言的本地化。

其间只有首先点是必要的。
其次点最好也可以实现,很多时候切换语言只为了看看某个专业术语在保加塞维利亚语中的原文是咋样,或者暂时打印个英文报表,平日采用或者用粤语,用户不想为了这点重启程序。
其三点和第四点虽然很常见,但自己向来没实现过,毕竟文字资源(有时还有少量图纸)占用的上空不会太多,大部分WPF程序都不曾大到需要考虑安装包大小,所有语言的资源总体打包进一个安装包就足以了。

WPF本地化技术很干练,也有两种方案,微软在MSDN给出了详细的牵线WPF
全球化和本地化概述
.aspx),还有一份古老的文档WPF
Localization
Guidance
,整整66页,里面详细介绍了各类WPF本地化的编制。

正文只介绍二种实现以上第1、2点需要的方案。

 析构函数又称终结器,用于析构类的实例。

2. 采纳资源词典

定义

2.1 基本原理

对WPF开发者来说,资源词典肯定不会陌生。可是在资源词典里拔取string可能相比较少。

<Window x:Class="LocalizationDemoWpf.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:LocalizationDemoWpf"
        mc:Ignorable="d" 
        xmlns:system="clr-namespace:System;assembly=mscorlib"
        Title="Window1" Height="300" Width="300">
    <Window.Resources>
        <system:String x:Key="Chinese">中文</system:String>
    </Window.Resources>
    <Grid>
        <TextBlock Text="{DynamicResource Chinese}"/>
    </Grid>
</Window>

如以上代码所示,在XAML中定义string资源需要先引入xmlns:system="clr-namespace:System;assembly=mscorlib"取名空间,之后再接纳DynamicResource引用这些资源。不要使用StaticResource,这样没法完成动态切换语言。

要利用资源词典实现本地化,需要先成立所需语言的xaml,我在DEMO中成立了en-us.xaml和zh-cn.xaml六个资源词典,里面的涵盖的资源布局同样(指数量和Key一样):

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
                    xmlns:system="clr-namespace:System;assembly=mscorlib"
                    xmlns:local="clr-namespace:LocalizationDemoWpf">
    <system:String x:Key="SwitchLanguage">切换语言</system:String>
    <system:String x:Key="Chinese">中文</system:String>
    <system:String x:Key="English">英文</system:String>
    <system:String x:Key="Username">用户名</system:String>
    <system:String x:Key="Sex">性别</system:String>
    <system:String x:Key="Address">地址</system:String>
    <SolidColorBrush x:Key="Background" Color="#88FF0000"/>
</ResourceDictionary>

在先后启动时依照CultureInfo.CurrentUICulture或部署项选用相应的资源词典,使用MergedDictionaries的法子加载到程序的资源聚合中:

var culture = ReadCultureFromConfig();
var cultureInfo = new System.Globalization.CultureInfo(culture);
Thread.CurrentThread.CurrentUICulture = cultureInfo;
Thread.CurrentThread.CurrentCulture = cultureInfo;


ResourceDictionary dictionary = new ResourceDictionary { Source = new Uri($@"Resources\{culture}.xaml", UriKind.RelativeOrAbsolute) };
Application.Current.Resources.MergedDictionaries[0] = dictionary;

这么本地化的职能就水到渠成了。

  析构函数(destructor)
构造函数反而,当对象截至其生命周期时(例如对象所在的函数已调用完毕),系统活动执行析构函数。析构函数往往用来做“清理善后”
的做事(例如在创设目的时用new开辟了一片内存空间,delete会自动调用析构函数后获释内存)。

2.2 动态切换语言

实质上上述方案已落实了动态切换语言。
XAML资源的引用原则是内外原则,这么些附近不仅指VisualTree上的前后,还指时间上的前后。后添加进资源词典的资源将替换从前的同名资源。使用DynamicResource而不是StaticResource,就是为了在资源被替换时能实时变更UI的来得。

 

2.3 设计时襄助

VisualStudio的XAML设计时补助对开发WPF程序至关首要,对本地化来说,设计时协理至关首要涵盖3有的:

  • 在编写XAML时得以拿走资源的智能感知
  • 有完全的设计视图
  • 在不同语言之间切换

运用资源词典实现本地化,只需在App.xaml中联合对应的资源词典即可得到完整的规划时帮忙。

<Application x:Class="LocalizationDemoWpf.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:LocalizationDemoWpf"
             xmlns:resource="clr-namespace:LocalizationDemoWpf.Resource;assembly=LocalizationDemoWpf.Resource"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/LocalizationDemoWpf;component/Resources/zh-cn.xaml"/>
                <!--<ResourceDictionary Source="/LocalizationDemoWpf;component/Resources/en-us.xaml"/>-->
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

管理 1

这段XAML只是为着增强设计时体验,没有也能经过编译。

 

2.4 在代码里拜访资源

在代码中访问资源相比较辛劳,需要了解资源的称呼,而且尚未智能感知,假若资源词典由第三方类库提供就会更麻烦。

var message = TryFindResource("SwitchLanguage") as string;
if (string.IsNullOrWhiteSpace(message) == false)
    MessageBox.Show(message);

析构函数简介

2.5 在代码里替换资源

private void OnReplaceString(object sender, RoutedEventArgs e)
{
    _totalReplace++;
    string content = "Replace " + _totalReplace;
    Resources["StringToReplace"] = content;
}

如上所示,在代码中替换资源极度简约,然而这种简单也带来了资源不可控的题材。

以C++语言为例:\[1\] 
析构函数名也应与类名相同,只是在函数名前边加一个位取反符~,例如~stud(),以分别于构造函数。它不可能带任何参数,也尚未再次回到值(包括void类型)。只可以有一个析构函数,不可能重载。假若用户没有编制析构函数,编译系统会自动生成一个缺省的析构函数(尽管自定义了析构函数,编译器也一连会为我们合成一个析构函数,并且只要自定义了析构函数,编译器在履行时会先调用自定义的析构函数再调用合成的析构函数),它也不举办其余操作。所以众多简便的类中绝非用显式的析构函数。

2.6 在先后集以内共享资源

下面有提过,在获取第三方类库中某个资源丰裕难为,不仅如此,连得到第三方类库中的资源词典名称都非常劳动。我提出在类库中定义如下的类,可以给开发者提供部分惠及:

public static class Resources
{
    public static Uri EnglishResourceUri { get; } =
        new Uri("/LocalizationDemoWpf.Resource;component/Resource.en-us.xaml", UriKind.RelativeOrAbsolute);

    public static Uri ChineseResourceUri { get; } =
        new Uri("/LocalizationDemoWpf.Resource;component/Resource.zh-cn.xaml", UriKind.RelativeOrAbsolute);
}

 

2.7 总结

资源词典是贯彻本地化的一种很普遍的方法,它有如下优点:

  • 简单易用,而且容易了解。
  • XAML语法简单。
  • 资源得以是除string以外的品种,如SolidColorBrush。

但这种方法的欠缺也不在少数:

  • 麻烦管理,一旦资源过多,重名、相互覆盖、智能感知列表过长等题材将极大地震慑开发,就连保证不同语言间资源词典里的资源数量同样都很麻烦。
  • 在先后集以内难以共享,引用很简单,但由于没有智能感知将很难使用,而且不同程序集以内的资源同名更麻烦跟踪。

除此以外,在动态切换语言上还设有部分题材。下面这段XAML就无奈完成动态切换语言:

<DataGrid Grid.Row="1" Margin="5">
    <DataGrid.Columns>
        <DataGridTextColumn Header="{DynamicResource Username}"/>
        <DataGridTextColumn Header="{DynamicResource Sex}"/>
        <DataGridTextColumn Header="{DynamicResource Address}" Width="*"/>
    </DataGrid.Columns>
</DataGrid>

在DataGridColumn的Header上做动态切换语言,需要写成DataTemplate的法门:

<DataGrid Grid.Row="2" Margin="5">
    <DataGrid.Columns>
        <DataGridTextColumn >
            <DataGridTextColumn.HeaderTemplate>
                <DataTemplate >
                    <TextBlock Text="{DynamicResource Username}"/
                </DataTemplate>
            </DataGridTextColumn.HeaderTemplate>
        </DataGridTextColumn>
        <DataGridTextColumn >
            <DataGridTextColumn.HeaderTemplate>
                <DataTemplate >
                    <TextBlock Text="{DynamicResource Sex}"/>
                </DataTemplate>
            </DataGridTextColumn.HeaderTemplate>
        </DataGridTextColumn>
        <DataGridTextColumn Width="*">
            <DataGridTextColumn.HeaderTemplate>
                <DataTemplate >
                    <TextBlock Text="{DynamicResource Address}"/>
                </DataTemplate>
            </DataGridTextColumn.HeaderTemplate>
        </DataGridTextColumn>
    </DataGrid.Columns>
</DataGrid>

 析构函数的接纳

3. 接纳Resx资源文件


3.1 基本原理

比起资源词典,我更欣赏使用Resx资源文件,然而这种措施语法复杂一些,而且也有无数小问题。
在VisualStudio中开创后缀名为resx的资源文件并开拓,可在偏下UI编辑资源文件的值(将拜访修饰符改为public用起来方便些):
管理 2

在改动资源文件的值后PublicResXFileCodeGenerator将电动创制对应的类并为每一个键值添加如下代码:

/// <summary>
///   查找类似 Address 的本地化字符串。
/// </summary>
public static string Address {
    get {
        return ResourceManager.GetString("Address", resourceCulture);
    }
}

然后将以此资源文件复制粘贴一份,将名称改为“原名+.+对应的语言+.resx”的格式,并且将内部的值翻译成对应语言如下:
管理 3

在UI上使用x:Static绑定到相应的资源:

<DataGridTextColumn Header="{x:Static local:Labels.Username}"/>

这样大旨的本地化就完了了。很多控件库都是运用那种模式做本地化。除了字符串,resx资源文件还帮忙除字符串以外的资源,如图片、音频等。
管理 4

然则这么些方案只兑现了最主旨的本地化,而且最大的问题是只协助直接选取字符串,不襄助TypeConverter,甚至也不匡助除字符串以外的此外XAML内置类型.aspx)(即Boolea,Char,Decimal,Single,Double,Int16,Int32,Int64,提姆(Tim)eSpan,Uri,Byte,Array等类别)。例如利用Label.resx中名为Background值为
#880000FF 的字符串为Grid.Background实现本地化:

Labels.designer.resx

/// <summary>
///   查找类似 #880000FF 的本地化字符串。
/// </summary>
public static string Background {
    get {
        return ResourceManager.GetString("Background", resourceCulture);
    }
}

MainWindow.xaml

<Grid  Background="{x:Static local:Labels.Background}"/>

运转时报错:ArgumentException:
“#88FF0000”不是性质“Background”的有效值。

这么资源文件的实用性大促销扣。当然,这些方案也不协理动态切换语言。

  • 不可能在构造中定义析构函数。只好对类使用析构函数。

  • 一个类只好有一个析构函数。

  • 无所适从继续或重载析构函数。

  • 没辙调用析构函数。它们是被活动调用的。

  • 析构函数既没有修饰符,也并未参数。

3.2 动态切换语言

Silverlight.aspx)中已没有了x:Static的绑定模式,改为使用Binding实现本地化,这样即便语法复杂一些,但更是实用。WPF当然也足以运用这种办法。

第一, 创造一个类包装资源文件生成的类(在这么些Demo中是Labels):

public class ApplicationResources
{
    public ApplicationResources()
    {
        Labels = new Labels();
    }

    public Labels Labels { get; set; }
}

然后在App.xaml上校以此类作为资源充足到资源集合中,为了将来使用的语法简单些,我通常将Key取得很简单:

<Application x:Class="LocalizationDemoWpfUsingResource.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:LocalizationDemoWpfUsingResource"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <local:ApplicationResources x:Key="R"  />
    </Application.Resources>
</Application>

最终在XAML中这样绑定:

<DataGridTextColumn Header="{Binding Labels.Username, Source={StaticResource R}}"/>

如此这般语法复杂一些,但也有广大益处:

  • 匡助TypeConverter,这样就可以运用除String以外的其余门类。
  • 支撑Binding的别样效率,如IValueConverter。

麻烦的是,WPF似乎不是很欣赏这种方法,VisualStudio会提醒这种似是而非,毕竟资源文件中的属性都是static属性,不是实例成员。幸运的是编译一回这种不当提醒就会不复存在。
管理 5

将调用模式改为Binding将来就足以兑现动态切换语言了。由于UI通过Binding获取资源文件的始末,可以经过INotifyPropertyChanged布告UI更新。将ApplicationResources
改造一下:

public class ApplicationResources : INotifyPropertyChanged
{
    public static ApplicationResources Current { get; private set; }

    public ApplicationResources()
    {
        Current = this;
        Labels = new Labels();
    }

    public Labels Labels { get; set; }



    public event PropertyChangedEventHandler PropertyChanged;

    public  void ChangeCulture(System.Globalization.CultureInfo cultureInfo)
    {
        Thread.CurrentThread.CurrentUICulture = cultureInfo;
        Thread.CurrentThread.CurrentCulture = cultureInfo;
        Labels.Culture = cultureInfo;

        if (Current != null)
            Current.RaiseProoertyChanged();
    }

    public void RaiseProoertyChanged()
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(""));
    }
}

今昔可以大概地切换语言了。

var culture = ReadCultureFromConfig();
var cultureInfo = new System.Globalization.CultureInfo(culture);
ApplicationResources.Current.ChangeCulture(cultureInfo);

 

3.3 设计时补助

贯彻本地化的一个很麻烦的事体是什么在统筹视图看到各个语言下的机能。在采取资源词典的方案中是通过在App.xaml中联合对应的资源词典:

<ResourceDictionary.MergedDictionaries>
    <ResourceDictionary Source="/LocalizationDemoWpf;component/Resources/zh-cn.xaml"/>
    <!--<ResourceDictionary Source="/LocalizationDemoWpf;component/Resources/en-us.xaml"/>-->
</ResourceDictionary.MergedDictionaries>

在资源文件的方案中,需要在ApplicationResources中添加一个性质:

private string _language;

/// <summary>
/// 获取或设置 Language 的值
/// </summary>
public string Language
{
    get { return _language; }
    set
    {
        if (_language == value)
            return;

        _language = value;
        var cultureInfo = new CultureInfo(value);
        Thread.CurrentThread.CurrentUICulture = cultureInfo;
        Thread.CurrentThread.CurrentCulture = cultureInfo;
        Labels.Culture = cultureInfo;

        RaiseProoertyChanged();
    }
}

而后在App.xaml中就可以通过改变这一个特性来改变规划时的UI的言语,在VS2017中连编译都不需要就能够变更计划视图的语言。

<local:ApplicationResources x:Key="R"  Language="zh-CN"/>

管理 6

声明:

3.4 在代码里拜访资源

在代码里拜访资源文件的资源非常简练:

MessageBox.Show(Labels.SwitchLanguage);
class Car
{
    ~ Car()  // destructor
    {
        // cleanup statements...
    }
}

3.5 在代码里替换资源

资源文件要贯彻这么些需要就一些都不好玩了,至少自己没有在骨子里工作中做过。最大的难题是资源文件生成的类中的属性是静态属性,而且只有getter方法:

public static string StringToReplace {
    get {
        return ResourceManager.GetString("StringToReplace", resourceCulture);
    }
}

咱俩也能够成立一个派生类,强行替换对应的性能:

public class ExtendLabels : Labels
{
    /// <summary>
    /// 获取或设置 StringToReplace 的值
    /// </summary>
    public new string StringToReplace { get; set; }
}

下一场替换ApplicationResources中的Labels,并且触发PropertyChanged。不过这样会刷新所有UI上的字符串等资源,只为了替换一个字符资源代价有点大,幸好一般的话并不会太耗费性能。

private void OnReplaceString(object sender, RoutedEventArgs e)
{
    _totalReplace++;
    string content = Labels.StringToReplace + " " + _totalReplace;
    if (_extendLabels == null)
        _extendLabels = new ExtendLabels();

    _extendLabels.StringToReplace = content;
    ApplicationResources.Current.Labels = _extendLabels;
    ApplicationResources.Current.RaiseProoertyChanged();
}

  该析构函数隐式地对目的的基类调用
Finalize.aspx)。这样,前边的析构函数代码被隐式地转换为:

3.6 在先后集以内共享资源

只需要将资源文件的拜会修饰符改为public,无需任何操作就足以方便地在程序集以内共享资源。
管理 7

protected override void Finalize()
{
    try
    {
        // cleanup statements...
    }
    finally
    {
        base.Finalize();
    }
}

3.7 管理资源文件

比起资源词典,资源文件还有一个很大的优势就是便于管理。Demo中只有一个名字Labels的资源文件,实际项目中得以按职能或模块分别创建相应的资源文件,解决了资源词典重名、相互覆盖、智能感知列表过长等问题。另外我推荐使用VS的扩张程序ResXManager管理所有资源文件。
管理 8

它可以在一个UI里管理所有语言的资源文件,极大地惠及了资源文件的运用。
管理 9

  这意味着对继承链中的所有实例递归地调用 Finalize
方法(从派生程度最大的到派生程度最小的)。

3.8 ReSharper支持

对Resx资源文件,ReSharper也提供了精粹的帮忙。

当需要为某个资源修改Key时,可以按“资源文件名称”+”.”+”Key”来全局替换,通常这样已经够用放心。Re夏普(Sharp)er更进一步,它提供了重命名功用。假使要将Labels的资源English重名为为Englishs,可以先在Labels.Designer.cs重命名,然后使用“Apply
rename refactoring”选项:
管理 10

此刻所有引用,包括XAML都已利用新的称呼:
管理 11

唯独最终仍需协调出手在资源文件编辑器中修改Key。

除去,假诺在XAML中采纳了不当的Key,ReSharper也有不当指示:
管理 12

在一些场馆,ReShaper还可使用“Move To Resource”功用:
管理 13
管理 14

注意

不应使用空析构函数。如果类包含析构函数,Finalize 队列中则会创建一个项。调用析构函数时,将调用垃圾回收器来处理该队列。如果析构函数为空,则只会导致不必要的性能丢失。

3.9 总结

使用Resx资源文件贯彻地点化有如下优点:

  • 资源管理有利于。
  • 容易在代码中采纳。
  • 容易在程序集以内共享。
  • 协理TypeConverter,这样就足以选取除String以外的别样项目。
  • 支撑Binding的任何效用,如IValueConverter。
  • 兼容性好,Silverlight及之后的XAML技术都得以接纳。
  • 其三方工具协助。
  • 辅助图片、音频等资源。

症结如下:

  • XAML语法相对复杂。
  • 不可能直接拔取于TypeConverter不襄助的品类,例如LinearGradientBrush。

虽说不可能一向援助LinearGradientBrush,但也不是一心没有章程,只是复杂了不少,如分别对LinearGradientBrush的GradientStop做本地化:

<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
    <GradientStop Color="Black" Offset="0"/>
    <GradientStop Color="{Binding Source={StaticResource R},Path=Labels.Background}" Offset="1"/>
</LinearGradientBrush>

程序员不可以控制哪天调用析构函数,因为这是由垃圾回收器决定的。垃圾回收器检查是不是留存应用程序不再利用的目标。尽管垃圾回收器认为某个对象符合析构,则调用析构函数(假设有)并回收用来储存此目的的内存。程序退出时也会调用析构函数。

4. 结语

这篇作品只介绍了本地化的入门知识,其余还有众多本地化的主旨,如验证信息中的本地化没有提到。此外,本地化仍可以够使用x:Uid情势或WPFLocalizeExtension等措施实现,那里就不详细介绍。
WPF
全球化和本地化概述
.aspx)里有介绍一些本地化的一级做法,如UI上理应运用绝对布局而非相对布局、字体选拔等,这里不再累赘。

亟需留意的是上述二种方案都不适用于CLR属性,这也是干吗自己一贯强调UIElement的属性最好是凭借属性的来头之一。

如有错漏请提议。

能够透过调用
Collect.aspx)
强制举办垃圾回收,但大多数场合下应制止这样做,因为这样会促成性能问题有关更多音信,请参见强制垃圾回收.aspx)。

5. 参考

WPF
全球化和本地化概述
.aspx)
Silverlight
部署和本地化
.aspx)
WPFLocalizationExtension
WPF Localization Guidance
XAML
Resources

CultureInfo
.aspx)
Supported
languages

 

6. 源码

LocalizationDemo

 使用析构函数释放资源

 平时,与运作时不开展垃圾回收的编程语言相比较,C#
无需太多的内存管理。这是因为 .NET Framework
垃圾回收器会隐式地管理对象的内存分配和释放。可是,当应用程序封装窗口、文件和网络连接这类非托管资源时,应当接纳析构函数释放这一个资源。当目的符合析构时,垃圾回收器将运行目的的
Finalize 方法。

 

资源的显式释放

假诺你的应用程序在应用昂贵的外部资源,则还指出你提供一种在废品回收器释放对象前显式地放出资源的措施。可由此兑现来源
IDisposable.aspx)
接口的 Dispose
方法来形成这或多或少,该情势为对象进行必要的清理。这样可大大提高应用程序的习性。即便有这种对资源的显式控制,析构函数也是一种珍惜措施,可用来在对
Dispose 方法的调用退步时清理资源。

 

示例

下边的言传身教创设五个类,这两个类构成了一个继承链。类
First 是基类,Second 是从 First 派生的,而 Third 是从 Second
派生的。这五个类都有析构函数。在 Main()
中,创制了派生程度最大的类的实例。注意:程序运行时,这多个类的析构函数将自行被调用,并且是比照从派生程度最大的到派生程度最小的顺序调用。

class First
{
    ~First()
    {
        System.Console.WriteLine("First's destructor is called");
    }
}

class Second: First
{
    ~Second()
    {
        System.Console.WriteLine("Second's destructor is called");
    }
}

class Third: Second
{
    ~Third()
    {
        System.Console.WriteLine("Third's destructor is called");
    }
}

class TestDestructors
{
    static void Main() 
    {
        Third t = new Third();
    }
}

 

 

Post Author: admin

发表评论

电子邮件地址不会被公开。 必填项已用*标注