Статические поля в AppDomain - PullRequest
30 голосов
/ 28 ноября 2010

Я экспериментирую с идеями об использовании AppDomain для управления устаревшим кодом, содержащим множество статических полей в многопоточной среде.

Я прочитал ответы на этот вопрос: Как использовать AppDomain для ограниченияобласть действия статического класса для поточно-ориентированного использования? , подумал, что это довольно многообещающе, и решил попробовать его с очень простым классом в сборке ClassLibrary1.dll:

namespace ClassLibrary1
{
    public static class Class1
    {
        private static int Value = 0;

        public static void IncrementAndPrint()
        {
            Console.WriteLine(Value++);
        }
    }
}

и вот мой код, которыйзагружает сборку в 2 разных домена приложения и несколько раз вызывает IncrementAndPrint ():

var appDomain1 = System.AppDomain.CreateDomain("AppDomain1");
var appDomain2 = System.AppDomain.CreateDomain("AppDomain2");

var assemblyInAppDomain1 = appDomain1.Load("ClassLibrary1");
var assemblyInAppDomain2 = appDomain2.Load("ClassLibrary1");

var class1InAppDomain1 = assemblyInAppDomain1.GetType("ClassLibrary1.Class1");
var class1InAppDomain2 = assemblyInAppDomain2.GetType("ClassLibrary1.Class1");

class1InAppDomain1.InvokeMember("IncrementAndPrint", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, null);
class1InAppDomain1.InvokeMember("IncrementAndPrint", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, null);
class1InAppDomain1.InvokeMember("IncrementAndPrint", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, null);

class1InAppDomain2.InvokeMember("IncrementAndPrint", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, null);
class1InAppDomain2.InvokeMember("IncrementAndPrint", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, null);
class1InAppDomain2.InvokeMember("IncrementAndPrint", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, null);

Я ожидал, что результат будет:

0
1
2
0
1
2

, потому что будет копиястатическое поле Значение для локального для каждого экземпляра AppDomain.Однако вместо этого я получил:

0
1
2
3
4
5

, который говорит мне, что они все еще используют одну и ту же копию статического поля Value.Может кто-нибудь сказать мне, что я сделал не так?

Обновление:

Я попробовал предложение Эрика, теперь я вызываю метод CreateInstanceAndUnwrap () класса AppDomain вместо вызоваLoad () и GetType (), как показано ниже.Кроме того, я преобразовал IncrementAndPrint в метод экземпляра, а не в статический метод.Тем не менее, я все еще получаю тот же результат.

var appDomain1 = System.AppDomain.CreateDomain("AppDomain1");
var appDomain2 = System.AppDomain.CreateDomain("AppDomain2");

var class1InAppDomain1 = (Class1)appDomain1.CreateInstanceAndUnwrap("ClassLibrary1", "ClassLibrary1.Class1");
var class1InAppDomain2 = (Class1)appDomain2.CreateInstanceAndUnwrap("ClassLibrary1", "ClassLibrary1.Class1");

class1InAppDomain1.IncrementAndPrint();
class1InAppDomain1.IncrementAndPrint();
class1InAppDomain1.IncrementAndPrint();

class1InAppDomain2.IncrementAndPrint();
class1InAppDomain2.IncrementAndPrint();
class1InAppDomain2.IncrementAndPrint();

1 Ответ

23 голосов
/ 28 ноября 2010

Похоже, вы загружаете тип из другого домена приложения в текущий домен приложения. Таким образом, код, который вызывает статические методы, вызывается из текущего домена приложения.

Мне неизвестен какой-либо другой способ вызова статического метода в другом домене без создания экземпляра объекта в другом домене и вызова этого объекта статическим методом.

Пример. Решение содержит 2 проекта (ClassLibrary и приложение Winforms / Console)

[ClassLibrary]

using System;

namespace MyLibrary
{
    public class DomainObject : MarshalByRefObject
    {
        private static int _Value;

        private static void IncrementValue()
        {
            DomainObject._Value++;
        }

        public static int Value
        {
            get
            {
                return DomainObject._Value;
            }
        }

        public int GetIncrementedValue()
        {
            DomainObject.IncrementValue();
            return DomainObject.Value;
        }
    }
}

[Приложение]

private void button1_Click(object sender, EventArgs e)
{
    AppDomain domain1 = AppDomain.CreateDomain("domain1");
    AppDomain domain2 = AppDomain.CreateDomain("domain2");

    DomainObject object1 = 
        domain1.CreateInstanceAndUnwrap("MyLibrary", "MyLibrary.DomainObject") 
        as DomainObject;

    DomainObject object2 = 
        domain2.CreateInstanceAndUnwrap("MyLibrary", "MyLibrary.DomainObject") 
        as DomainObject;

    if (object1 != null)
    {
        Console.WriteLine("object 1 Value = " 
                          + object1.GetIncrementedValue().ToString());
        Console.WriteLine("object 1 Value = " 
                          + object1.GetIncrementedValue().ToString());
        Console.WriteLine("object 1 Value = " 
                          + object1.GetIncrementedValue().ToString());
    }
    if (object2 != null)
    {
        Console.WriteLine("object 2 Value = "
                          + object2.GetIncrementedValue().ToString());
        Console.WriteLine("object 2 Value = "
                          + object2.GetIncrementedValue().ToString());
        Console.WriteLine("object 2 Value = "
                          + object2.GetIncrementedValue().ToString());
    }

    /* Unload the Domain and re-create
     * This should reset the Static Value in the AppDomain
     */
    AppDomain.Unload(domain1);
    domain1 = AppDomain.CreateDomain("domain1");
    object1 = domain1.CreateInstanceAndUnwrap("MyLibrary", 
                                              "MyLibrary.DomainObject") 
                                              as DomainObject;

    if (object1 != null)
    {
        Console.WriteLine("object 1 Value = "
                          + object1.GetIncrementedValue().ToString());
        Console.WriteLine("object 1 Value = "
                          + object1.GetIncrementedValue().ToString());
        Console.WriteLine("object 1 Value = "
                          + object1.GetIncrementedValue().ToString());
    }
    if (object2 != null)
    {
        Console.WriteLine("object 2 Value = "
                          + object2.GetIncrementedValue().ToString());
        Console.WriteLine("object 2 Value = "
                          + object2.GetIncrementedValue().ToString());
        Console.WriteLine("object 2 Value = "
                          + object2.GetIncrementedValue().ToString());
    }
}

Сгенерированные результаты:

object 1 Value = 1
object 1 Value = 2
object 1 Value = 3
object 2 Value = 1
object 2 Value = 2
object 2 Value = 3
object 1 Value = 1
object 1 Value = 2
object 1 Value = 3
object 2 Value = 4
object 2 Value = 5
object 2 Value = 6
...