cari
Rumahpangkalan datatutorial mysql一个关于解决序列化问题的编程技巧

一个关于解决序列化问题的编程技巧

Jun 07, 2016 pm 03:10 PM
kira-kirabersiriartikelKemahiran pengaturcaraanselesaikan

在前一篇文章中我曾经说过,现在正在做一个小小的框架以实现采用统一的API实现对上下文(Context)信息的统一管理。这个框架同时支持Web和GUI应用,并支持跨线程传递和跨域传递(这里指在WCF服务调用中实现客户端到服务端隐式传递),以及对上下文项目(Cont

在前一篇文章中我曾经说过,现在正在做一个小小的框架以实现采用统一的API实现对上下文(Context)信息的统一管理。这个框架同时支持Web和GUI应用,并支持跨线程传递和跨域传递(这里指在WCF服务调用中实现客户端到服务端隐式传递),以及对上下文项目(ContextItem)的读写控制。关键就在于后面两个特性的支持上面,出现一个小小的关于序列化的问题。解决方案只需要改动短短的一行代码,结果却让我折腾了老半天。

一、问题重现

为了重现我实际遇到的问题,我特意将问题简化,为此我写了一个简单的例子(你可以从这里下载)。在下面的代码片断中,我创建了一个名称为ContextItem的类型,代表一个需要维护的上下文项。由于需要在WCF服务调用实现自动传递,我将起定义成DataContract。ContextItem包含Key,Value和ReadOnly三个属性,不用说ReadOnly表示该ContextItem可以被修改。注意Value属性Set方法的定义——如果ReadOnly则抛出异常。

<span>   1:</span> [DataContract(Namespace = <span>"http://www.artech.com"</span>)]
<span>   2:</span> <span>public</span> <span>class</span> ContextItem
<span>   3:</span> {
<span>   4:</span>     <span>private</span> <span>object</span> <span>value</span> = <span>null</span>;
<span>   5:</span>     [DataMember]
<span>   6:</span>     <span>public</span> <span>string</span> Key { get; <span>private</span> set; }
<span>   7:</span>     [DataMember]
<span>   8:</span>     <span>public</span> <span>object</span> Value
<span>   9:</span>     {
<span>  10:</span>         get
<span>  11:</span>         {
<span>  12:</span>             <span>return</span> <span>this</span>.<span>value</span>;
<span>  13:</span>         }
<span>  14:</span>         set
<span>  15:</span>         {
<span>  16:</span>             <span>if</span> (<span>this</span>.ReadOnly)
<span>  17:</span>             {
<span>  18:</span>                 <span>throw</span> <span>new</span> InvalidOperationException(<span>"Cannot change the value of readonly context item."</span>);
<span>  19:</span>             }
<span>  20:</span>             <span>this</span>.<span>value</span> = <span>value</span>;
<span>  21:</span>         }
<span>  22:</span>     }
<span>  23:</span>     [DataMember]
<span>  24:</span>     <span>public</span> <span>bool</span> ReadOnly { get; set; }
<span>  25:</span>     <span>public</span> ContextItem(<span>string</span> key, <span>object</span> <span>value</span>)
<span>  26:</span>     {
<span>  27:</span>         <span>if</span> (<span>string</span>.IsNullOrEmpty(key))
<span>  28:</span>         {
<span>  29:</span>             <span>throw</span> <span>new</span> ArgumentNullException(<span>"key"</span>);
<span>  30:</span>         }
<span>  31:</span>         <span>this</span>.Key = key;
<span>  32:</span>         <span>this</span>.Value = <span>value</span>;
<span>  33:</span>     }
<span>  34:</span> }

为了演示序列化和反序列化,我写了如下两个静态的帮助方法。Serialize和Deserialize分别用于序列化和反序列化,前者将对象序列成成XML并保存到指定的文件中,后者则从文件读取XML并反序列化成相应的对象。

<span>   1:</span> <span>public</span> <span>static</span> T Deserialize<t>(<span>string</span> fileName)</t>
<span>   2:</span> {
<span>   3:</span>     DataContractSerializer serializer = <span>new</span> DataContractSerializer(<span>typeof</span>(T));
<span>   4:</span>     <span>using</span> (XmlReader reader = <span>new</span> XmlTextReader(fileName))
<span>   5:</span>     {
<span>   6:</span>         <span>return</span> (T)serializer.ReadObject(reader);
<span>   7:</span>     }
<span>   8:</span> }
<span>   9:</span>  
<span>  10:</span> <span>public</span> <span>static</span> <span>void</span> Serialize<t>(T instance, <span>string</span> fileName)</t>
<span>  11:</span> {
<span>  12:</span>     DataContractSerializer serializer = <span>new</span> DataContractSerializer(<span>typeof</span>(T));
<span>  13:</span>     <span>using</span> (XmlWriter writer = <span>new</span> XmlTextWriter(fileName, Encoding.UTF8))
<span>  14:</span>     {
<span>  15:</span>         serializer.WriteObject(writer, instance);
<span>  16:</span>     } 
<span>  17:</span>     Process.Start(fileName);
<span>  18:</span> }

我们的程序很简单。从如下的代码片断中,我们先创建一个ContextItem对象,然后将ReadOnly属性设置成true。然后调用Serialize方法将对象序列化成XML并保存在一个名称为context.xml的文件中。然后调用Deserialize方法,读取该文件进行反序列化。

<span>   1:</span> <span>static</span> <span>void</span> Main(<span>string</span>[] args)
<span>   2:</span> {
<span>   3:</span>     var contextItem1 = <span>new</span> ContextItem(<span>"__userId"</span>, <span>"Foo"</span>);
<span>   4:</span>     contextItem1.ReadOnly = <span>true</span>;
<span>   5:</span>     Serialize<contextitem>(contextItem1, <span>"context.xml"</span>);</contextitem>
<span>   6:</span>     var contextItem2 = Deserialize<contextitem>(<span>"context.xml"</span>);           </contextitem>
<span>   7:</span> }

序列化操作能够正常执行,但当程序执行到Deserialize的时候抛出如下一个InvalidOperationException异常。

一个关于解决序列化问题的编程技巧

二、问题分析

从上面给出的截图,我们不难看出,异常是在给ContextItem对象的Value属性赋值的时候抛出的。如果对DataContractSerializer序列化器的序列化/反序列化规则的有所了解的话,应该知道:对于数据契约(DataContract)基于属性(Property)的数据成员(DataMember),序列器在反序列化的时候是通过调用Set方法对其进行初始化的。在本例中,由于ReadOnly是True,在对Value进行反序列化的时候必然会调用Set方法。但是,只读的ContextItem却不能对其赋值,所以异常抛出。

那么,如何来解决这个问题呢?我最初的想法是这样:在序列化的时候将ReadOnly属性设置成False,然后添加另一个属性专门用于保存真实的值。在进行反序列的时候,由于ReadOnly为false,所以不会出现异常。当反序列化完成之后,在将ReadOnly的初始值赋上。虽然上述的方案能够解决问题,但是为此对ContextItem添加一个只在序列化和反序列化的过程中在有用的属性,总觉得很丑陋。

我们不妨换一种思路:异常产生于对Value属性凡序列化时发现ReadOnly非True的情况。那么怎样采用避免这种情况的发生呢?如果Value属性先于ReadOnly属性被序列化,那么ReadOnly的初始值就是False,这个问题不就解决了吗?这就是我们的第一个解决方案。

三、解决方案一:通过控制属性反序列化顺序

那么,如果控制那么属性先被反序列化,那么后被序列化呢?这就是要了解DataContractSerializer序列化器的序列化和发序列化规则了。在默认的情况下,DataContractSerializer是按照数据成员的名称的顺序进行序列化的。这可以从生成出来的XML的结构看出来。而XML元素的先后顺序决定了反序列化的顺序。

<span>   1:</span> <span><span>ContextItem</span> <span>xmlns:i</span><span>="http://www.w3.org/2001/XMLSchema-instance"</span> <span>xmlns</span><span>="http://www.artech.com"</span><span>></span></span>
<span>   2:</span>     <span><span>Key</span><span>></span>__userId<span></span><span>Key</span><span>></span></span>
<span>   3:</span>     <span><span>ReadOnly</span><span>></span>true<span></span><span>ReadOnly</span><span>></span></span>
<span>   4:</span>     <span><span>Value</span> <span>xmlns:d2p1</span><span>="http://www.w3.org/2001/XMLSchema"</span> <span>i:type</span><span>="d2p1:string"</span><span>></span>Foo<span></span><span>Value</span><span>></span></span>
<span>   5:</span> <span></span><span>ContextItem</span><span>></span>

在上面的例子中,ContextItem的ReadOnly排在Value的前面,会先被序列化。那么,是不是我们要更新Value或者ReadOnly的数据成员(DataMember,不是属性名称)呢?这肯定不是我们想要的解决方案。在SOA的世界中,DataMember是契约的一部分,往往是不容许更改的。

如果在不更改数据成员名称的前提下让属性Value先于ReadOnly被序列化,需要用到DataContractSerializer另一条反序列化规则:我们可以通过DataMemberAttribute特性的Order属性控制序列化后的属性在XML元素列表中的位置。

为此,我们有了答案,我们只需要将ContextItem稍加改动就可以了。在如下的代码中,在为Value和ReadOnly两个属性应用DataMemberAttribute的时候,将Order属性分别设置成1和2,这样就能使ContextItem对象在被序列化的时候,Value和ReadOnly属性对应的XML元素将永远会有前后之分。这里还需要注意的是,在Value属性的Set方法中,判断是否只读,采用的不是ReadOnly属性,而是对应的readonly字段。这一点非常重要,如果调用ReadOnly属性将会迫使该属性被反序列化。

<span>   1:</span> [DataContract(Namespace = <span>"http://www.artech.com"</span>)]
<span>   2:</span> <span>public</span> <span>class</span> ContextItem
<span>   3:</span> {
<span>   4:</span>     <span>private</span> <span>object</span> <span>value</span> = <span>null</span>;
<span>   5:</span>     <span>private</span> <span>bool</span> readOnly;
<span>   6:</span>     [DataMember]
<span>   7:</span>     <span>public</span> <span>string</span> Key { get; <span>private</span> set; }
<span>   8:</span>  
<span>   9:</span>     [DataMember(Order = 1)]
<span>  10:</span>     <span>public</span> <span>object</span> Value
<span>  11:</span>     {
<span>  12:</span>         get
<span>  13:</span>         {
<span>  14:</span>             <span>return</span> <span>this</span>.<span>value</span>;
<span>  15:</span>         }
<span>  16:</span>         set
<span>  17:</span>         {
<span>  18:</span>             <span>if</span> (<span>this</span>.readOnly)
<span>  19:</span>             {
<span>  20:</span>                 <span>throw</span> <span>new</span> InvalidOperationException(<span>"Cannot change the value of readonly context item."</span>);
<span>  21:</span>             }
<span>  22:</span>             <span>this</span>.<span>value</span> = <span>value</span>;
<span>  23:</span>         }
<span>  24:</span>     }
<span>  25:</span>     [DataMember(Order =2)]
<span>  26:</span>     <span>public</span> <span>bool</span> ReadOnly
<span>  27:</span>     {
<span>  28:</span>         get
<span>  29:</span>         {
<span>  30:</span>             <span>return</span> readOnly;
<span>  31:</span>         }
<span>  32:</span>         set
<span>  33:</span>         {
<span>  34:</span>             readOnly = <span>value</span>;
<span>  35:</span>         }
<span>  36:</span>     }
<span>  37:</span>     <span>//Others</span>
<span>  38:</span> }

有兴趣的读者可以亲自试试看,如果我们进行了如上的更改,前面的程序就能正常运行了。到这里,有的读者可以要问了,你不是说仅仅有一行代码的变化吗,我看上面改动的不止一行嘛。没有错,我们完全可以作更少的更改来解决问题。

四、解决方案二:将数据成员定义在字段上而不是属性上

我们再换一种思维,之所以出现异常是在反序列化的时候调用Value属性的Set方法所致。如果在反序列化的时候不调用这个方法不就得了吗?那么,如何才能避免对Value属性的Set方法的调用呢?方法很简单,那就是将数据成员定义在字段上,而不是属性上。基于属性的数据成员在反序列化的时候不得不通过调用Set方法对数据项进行初始化,而基于字段的数据成员在反序列化的时候只需要直接对其复制就可以了。

基于这样的思路,我们对原来的ContextItem进行简单的改动——将DataMemberAttribute特性从Value属性移到value字段上。需要注意的,为了符合于原来的Schema,需要将DataMemberAttribute特性的Name属性设置成“Value”。

<span>   1:</span> [DataContract(Namespace = <span>"http://www.artech.com"</span>)]
<span>   2:</span> <span>public</span> <span>class</span> ContextItem
<span>   3:</span> {
<span>   4:</span>     [DataMember]
<span>   5:</span>     <span>public</span> <span>string</span> Key { get; <span>private</span> set; }
<span>   6:</span>  
<span>   7:</span>     [DataMember(Name = <span>"Value"</span>)]
<span>   8:</span>     <span>private</span> <span>object</span> <span>value</span> = <span>null</span>;
<span>   9:</span>     <span>public</span> <span>object</span> Value
<span>  10:</span>     {
<span>  11:</span>         get
<span>  12:</span>         {
<span>  13:</span>             <span>return</span> <span>this</span>.<span>value</span>;
<span>  14:</span>         }
<span>  15:</span>         set
<span>  16:</span>         {
<span>  17:</span>             <span>if</span> (<span>this</span>.ReadOnly)
<span>  18:</span>             {
<span>  19:</span>                 <span>throw</span> <span>new</span> InvalidOperationException(<span>"Cannot change the value of readonly context item."</span>);
<span>  20:</span>             }
<span>  21:</span>             <span>this</span>.<span>value</span> = <span>value</span>;
<span>  22:</span>         }
<span>  23:</span>     }
<span>  24:</span>     [DataMember]
<span>  25:</span>     <span>public</span> <span>bool</span> ReadOnly { get; set; }     
<span>  26:</span>      <span>//Others</span>
<span>  27:</span>     }
<span>  28:</span> }

总结

虽然这仅仅是一个很小的问题,解决的方案看起来也是如此的简单。但是,这并不意味着这是一个可以被忽视的问题,背后隐藏对DataMemberAttribute序列化的序列化规则的理解。

作者:Artech
出处:http://artech.cnblogs.com

Kenyataan
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
MySQL: Pengenalan kepada pangkalan data paling popular di duniaMySQL: Pengenalan kepada pangkalan data paling popular di duniaApr 12, 2025 am 12:18 AM

MySQL adalah sistem pengurusan pangkalan data relasi sumber terbuka, terutamanya digunakan untuk menyimpan dan mengambil data dengan cepat dan boleh dipercayai. Prinsip kerjanya termasuk permintaan pelanggan, resolusi pertanyaan, pelaksanaan pertanyaan dan hasil pulangan. Contoh penggunaan termasuk membuat jadual, memasukkan dan menanyakan data, dan ciri -ciri canggih seperti Operasi Join. Kesalahan umum melibatkan sintaks SQL, jenis data, dan keizinan, dan cadangan pengoptimuman termasuk penggunaan indeks, pertanyaan yang dioptimumkan, dan pembahagian jadual.

Kepentingan MySQL: Penyimpanan Data dan PengurusanKepentingan MySQL: Penyimpanan Data dan PengurusanApr 12, 2025 am 12:18 AM

MySQL adalah sistem pengurusan pangkalan data sumber terbuka yang sesuai untuk penyimpanan data, pengurusan, pertanyaan dan keselamatan. 1. Ia menyokong pelbagai sistem operasi dan digunakan secara meluas dalam aplikasi web dan bidang lain. 2. Melalui seni bina pelanggan-pelayan dan enjin penyimpanan yang berbeza, MySQL memproses data dengan cekap. 3. Penggunaan asas termasuk membuat pangkalan data dan jadual, memasukkan, menanyakan dan mengemas kini data. 4. Penggunaan lanjutan melibatkan pertanyaan kompleks dan prosedur yang disimpan. 5. Kesilapan umum boleh disahpepijat melalui pernyataan yang dijelaskan. 6. Pengoptimuman Prestasi termasuk penggunaan indeks rasional dan pernyataan pertanyaan yang dioptimumkan.

Mengapa menggunakan mysql? Faedah dan kelebihanMengapa menggunakan mysql? Faedah dan kelebihanApr 12, 2025 am 12:17 AM

MySQL dipilih untuk prestasi, kebolehpercayaan, kemudahan penggunaan, dan sokongan komuniti. 1.MYSQL Menyediakan fungsi penyimpanan dan pengambilan data yang cekap, menyokong pelbagai jenis data dan operasi pertanyaan lanjutan. 2. Mengamalkan seni bina pelanggan-pelayan dan enjin penyimpanan berganda untuk menyokong urus niaga dan pengoptimuman pertanyaan. 3. Mudah digunakan, menyokong pelbagai sistem operasi dan bahasa pengaturcaraan. 4. Mempunyai sokongan komuniti yang kuat dan menyediakan sumber dan penyelesaian yang kaya.

Huraikan mekanisme penguncian InnoDB (kunci yang dikongsi, kunci eksklusif, kunci niat, kunci rekod, kunci jurang, kunci seterusnya).Huraikan mekanisme penguncian InnoDB (kunci yang dikongsi, kunci eksklusif, kunci niat, kunci rekod, kunci jurang, kunci seterusnya).Apr 12, 2025 am 12:16 AM

Mekanisme kunci InnoDB termasuk kunci bersama, kunci eksklusif, kunci niat, kunci rekod, kunci jurang dan kunci utama seterusnya. 1. Kunci dikongsi membolehkan urus niaga membaca data tanpa menghalang urus niaga lain dari membaca. 2. Kunci eksklusif menghalang urus niaga lain daripada membaca dan mengubah suai data. 3. Niat Kunci mengoptimumkan kecekapan kunci. 4. Rekod Rekod Kunci Kunci Rekod. 5. Gap Lock Locks Index Rakaman Gap. 6. Kunci kunci seterusnya adalah gabungan kunci rekod dan kunci jurang untuk memastikan konsistensi data.

Apakah sebab -sebab biasa prestasi pertanyaan MySQL yang lemah?Apakah sebab -sebab biasa prestasi pertanyaan MySQL yang lemah?Apr 12, 2025 am 12:11 AM

Sebab -sebab utama prestasi pertanyaan MySQL yang lemah termasuk tidak menggunakan indeks, pemilihan pelan pelaksanaan yang salah oleh pengoptimasi pertanyaan, reka bentuk jadual yang tidak munasabah, jumlah data yang berlebihan dan persaingan kunci. 1. Tiada indeks menyebabkan pertanyaan perlahan, dan menambah indeks dapat meningkatkan prestasi dengan ketara. 2. Gunakan perintah Jelaskan untuk menganalisis pelan pertanyaan dan cari ralat pengoptimuman. 3. Membina semula struktur meja dan mengoptimumkan keadaan gabungan dapat meningkatkan masalah reka bentuk jadual. 4. Apabila jumlah data adalah besar, pembahagian dan strategi bahagian meja diterima pakai. 5. Dalam persekitaran konkurensi yang tinggi, mengoptimumkan urus niaga dan strategi mengunci dapat mengurangkan persaingan kunci.

Bilakah anda harus menggunakan indeks komposit berbanding indeks lajur tunggal?Bilakah anda harus menggunakan indeks komposit berbanding indeks lajur tunggal?Apr 11, 2025 am 12:06 AM

Dalam pengoptimuman pangkalan data, strategi pengindeksan hendaklah dipilih mengikut keperluan pertanyaan: 1. Apabila pertanyaan melibatkan pelbagai lajur dan urutan syarat ditetapkan, gunakan indeks komposit; 2. Apabila pertanyaan melibatkan pelbagai lajur tetapi urutan syarat tidak ditetapkan, gunakan pelbagai indeks lajur tunggal. Indeks komposit sesuai untuk mengoptimumkan pertanyaan berbilang lajur, manakala indeks lajur tunggal sesuai untuk pertanyaan tunggal lajur.

Bagaimana untuk mengenal pasti dan mengoptimumkan pertanyaan perlahan di MySQL? (Log pertanyaan perlahan, prestasi_schema)Bagaimana untuk mengenal pasti dan mengoptimumkan pertanyaan perlahan di MySQL? (Log pertanyaan perlahan, prestasi_schema)Apr 10, 2025 am 09:36 AM

Untuk mengoptimumkan pertanyaan perlahan MySQL, SlowQuerylog dan Performance_Schema perlu digunakan: 1. Dayakan SlowQueryLog dan tetapkan ambang untuk merakam pertanyaan perlahan; 2. Gunakan Performance_Schema untuk menganalisis butiran pelaksanaan pertanyaan, cari kesesakan prestasi dan mengoptimumkan.

MySQL dan SQL: Kemahiran Penting untuk PemajuMySQL dan SQL: Kemahiran Penting untuk PemajuApr 10, 2025 am 09:30 AM

MySQL dan SQL adalah kemahiran penting untuk pemaju. 1.MYSQL adalah sistem pengurusan pangkalan data sumber terbuka, dan SQL adalah bahasa standard yang digunakan untuk mengurus dan mengendalikan pangkalan data. 2.MYSQL menyokong pelbagai enjin penyimpanan melalui penyimpanan data yang cekap dan fungsi pengambilan semula, dan SQL melengkapkan operasi data yang kompleks melalui pernyataan mudah. 3. Contoh penggunaan termasuk pertanyaan asas dan pertanyaan lanjutan, seperti penapisan dan penyortiran mengikut keadaan. 4. Kesilapan umum termasuk kesilapan sintaks dan isu -isu prestasi, yang boleh dioptimumkan dengan memeriksa penyataan SQL dan menggunakan perintah menjelaskan. 5. Teknik pengoptimuman prestasi termasuk menggunakan indeks, mengelakkan pengimbasan jadual penuh, mengoptimumkan operasi menyertai dan meningkatkan kebolehbacaan kod.

See all articles

Alat AI Hot

Undresser.AI Undress

Undresser.AI Undress

Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover

AI Clothes Remover

Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool

Undress AI Tool

Gambar buka pakaian secara percuma

Clothoff.io

Clothoff.io

Penyingkiran pakaian AI

AI Hentai Generator

AI Hentai Generator

Menjana ai hentai secara percuma.

Artikel Panas

R.E.P.O. Kristal tenaga dijelaskan dan apa yang mereka lakukan (kristal kuning)
3 minggu yang laluBy尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Tetapan grafik terbaik
3 minggu yang laluBy尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Cara Memperbaiki Audio Jika anda tidak dapat mendengar sesiapa
3 minggu yang laluBy尊渡假赌尊渡假赌尊渡假赌
WWE 2K25: Cara Membuka Segala -galanya Di Myrise
4 minggu yang laluBy尊渡假赌尊渡假赌尊渡假赌

Alat panas

Muat turun versi mac editor Atom

Muat turun versi mac editor Atom

Editor sumber terbuka yang paling popular

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

Persekitaran pembangunan bersepadu PHP yang berkuasa

DVWA

DVWA

Damn Vulnerable Web App (DVWA) ialah aplikasi web PHP/MySQL yang sangat terdedah. Matlamat utamanya adalah untuk menjadi bantuan bagi profesional keselamatan untuk menguji kemahiran dan alatan mereka dalam persekitaran undang-undang, untuk membantu pembangun web lebih memahami proses mengamankan aplikasi web, dan untuk membantu guru/pelajar mengajar/belajar dalam persekitaran bilik darjah Aplikasi web keselamatan. Matlamat DVWA adalah untuk mempraktikkan beberapa kelemahan web yang paling biasa melalui antara muka yang mudah dan mudah, dengan pelbagai tahap kesukaran. Sila ambil perhatian bahawa perisian ini

Versi Mac WebStorm

Versi Mac WebStorm

Alat pembangunan JavaScript yang berguna

Pelayar Peperiksaan Selamat

Pelayar Peperiksaan Selamat

Pelayar Peperiksaan Selamat ialah persekitaran pelayar selamat untuk mengambil peperiksaan dalam talian dengan selamat. Perisian ini menukar mana-mana komputer menjadi stesen kerja yang selamat. Ia mengawal akses kepada mana-mana utiliti dan menghalang pelajar daripada menggunakan sumber yang tidak dibenarkan.