C# - 重新初始化静态类?

9
我正在开发的web应用程序中有一个保存客户端设置的类。需要说明的是,我不拥有这个类,也不能对它进行更改。最近我们添加了一些逻辑来将这些设置存储在数据库中,并且我被指派创建一个页面来编辑它们,这很公平。
然而,这里出现了一个问题:这些设置保存在一个静态类中,并且它们本身是静态、只读属性。例如:
public static class Settings
{
 public static readonly setting1 = SettingmanagerClass.GetSetting("setting1");
 public static readonly setting2 = SettingmanagerClass.GetSetting("setting2");
 public static readonly setting3 = SettingmanagerClass.GetSetting("setting3");
}

现在,举个例子,通过我编写的页面,我们将setting2的值更改为“Happy Variable”;它可以很好地保存到数据库中,但现在我需要在Web应用程序中反映出新值。由于它是静态类的静态只读属性,因此仅在应用程序首次连接时调用,无法手动设置。
再次强调,我不拥有原始类,因此“只需使属性可写”目前不是有效选项。通常,我会与我的老板商量,他会做出判断并可能允许我修改其他类,但我没有权利做出这个决定,而他这周不在办公室。
因此,基本上;是否有任何方法可以在Web应用程序开始运行后重新初始化静态类?我只需要它重新加载所有属性,就像应用程序刚刚重建并启动一样。

你可以使用反射来改变它或者回收这个过程。这里有一个线程讲述如何使用反射。 - zeal
只是一点小提示:你知道所有设置(例如来自web.config)都被缓存了吗? - Tim Schmelter
你能否重启你的IIS?[启动或停止应用程序池(IIS 7)] (http://technet.microsoft.com/en-us/library/cc732742(v=WS.10).aspx) - Steve
最佳选择是反射或弹回IIS AppDomain以清除静态成员。这两种方法都在下面详细说明了。当你的老板回来时,请获得批准并采取适当的解决方案,摆脱static readonly成员。要小心,我已经看到并仍然看到许多比我的编程生涯更老的“临时修补程序”。 - Adam Houldsworth
1
实际上,任何建议都比让它们可写更丑陋,我知道你说这“目前不是一个有效的选项”,但这将是最干净的选择... - James Michael Hare
显示剩余2条评论
9个回答

16
  ConstructorInfo constructor = typeof(Settings).GetConstructor(BindingFlags.Static | BindingFlags.NonPublic,null, new Type[0], null);
  constructor.Invoke(null, null);

谢谢。我已经测试了OP的类,以确保它是正确的 :) - Nesim Razon
我尝试了这个方法,它绝对有效,但是由于某种奇怪的原因,Web应用程序在调试器显示更新值的同时,仍然加载页面中的原始值。非常奇怪,虽然这次我没有使用它,但我会保留它,即使现在它对我来说纯粹是出于学术目的。 - ddietle
2
那真是邪恶。我以为我必须重构我的缓存类,但它就像黑魔法一样,谢谢。 - johnc
1
谢谢。这虽然很邪恶,但它起作用。反射很可怕。 - Aymen

5
您可以使用反射:
var prop = typeof(Settings).GetField("setting1", BindingFlags.Static | 
                                                 BindingFlags.Public);
prop.SetValue(null, "Bar");
string currentValue = Settings.setting1; //Bar

2
如果上面的代码代表了你所处的情况,那么除非你使用反射进行特别的黑客操作(顺便说一下,这并不是推荐的方法),否则你将无法重新初始化代码。
编辑:哦等等——我没有意识到这是一个 Web 应用程序。你可以通过编程弹出应用程序来实现:
System.Web.HttpRuntime.UnloadAppDomain

这可能会很危险,如果其他用户在系统中,并且由于跳动而丢失了会话。 - tsells
我刚刚对照我在原帖中提到的情景进行了测试,而且它成功了;没有任何会话丢失,只是重新加载了设置。完美。我已经测试了几次,以确保我不是因为认为自己成功而兴奋,但从我所看到的来看,这是纯粹的胜利,谢谢! - ddietle
@ddietle - 很高兴这对你有用,但我很困惑你的会话为什么没有被销毁。你是将会话存储在进程外(例如 SQL Server)吗? - RQDQ
这正是他们处理它的方式(SQL状态服务器)。我想这只是我的运气问题。 - ddietle
更新:我(幸运地)得到了处理这个问题的“正确”方式的许可,我将把属性从只读更改为具有get和set的标准属性。 松了一口气 - ddietle
顺便说一句 - 您不必使属性本身可写... 如果您只是使用jp2code给您的方法,问题会自行解决。 - RQDQ

1
唯一我能想到的选项需要大量工作:
  1. 创建另一个AppDomain
  2. 在其他域中加载程序集
  3. 使用Remoting获取数据
  4. 如果设置更改,请卸载AppDomain并重新执行步骤1到3

1
public static class Settings
{
   public static name = "";

   static Settings()
   {
    ReInitialize();
   }

   public static void ReInitialize()
   {
      name = "My name is re-initialized";
   }
}


Settings.name = "My name has changed";
// Console.WriteLine(Settings.name);
Settings.ReInitialize(); //name is "My name is re-initialized"
// Console.WriteLine(Settings.name);

1

我会使用反射

var info = typeof(Settings)
.GetField("Settings",BindingFlags.Static|BindingFlags.Public);
info.SetValue(null, "setting4");

0

虽然这是一个旧帖子,但我做的一件事是创建一个Initialize方法(public static void),它设置所有变量(它们都是public static)。在该方法中,进行数据库调用并设置类的变量。然后在代码中,每当您想要刷新变量时(即每当您调用SaveChanges()时),可以调用Class.Initialize()并完成操作。

我将其用于缓存常见的查找信息列表,这些信息可能会更改,并且我们需要保持与应用程序更新的数据库同步。


0

嗯,你想找到一种黑客班级的方法吗?即使使用反射等方式存在,这也不是解决问题的好方法。

我可以建议一个快速的解决方法,就是创建自己的非只读静态属性,用静态变量初始化它们,并在任何地方使用它们。

但最好使用缓存或应用程序存储而不是静态变量。

希望这可以帮助到你。


使用会话并不适合存储整个应用程序范围内的数据。此外,OP 表示他不拥有有问题的代码。 - RQDQ
编辑过了,当然可以使用应用程序或缓存更好。 - Arsen Mkrtchyan

-1

将您的值更改为属性:

public static class Settings
{
 public static setting1
 {
   get { return SettingmanagerClass.GetSetting("setting1"); }
 }
 public static setting2
 {
   get { return SettingmanagerClass.GetSetting("setting2");
 }
 public static setting3
 {
   get { return SettingmanagerClass.GetSetting("setting3");
 }
}

此外,这不会改变您代码的签名。


他说:“当前不是有效选项。”我编写的代码使字段保持“只读”(如果这是客户或规格要求)。必须有所改变才能提供此功能。 - jp2code
2
只是想澄清一下,我没有投反对票。无论如何,关键在于确切的措辞。如果楼主能够编辑代码,那么这个问题很可能就不存在了。 - Adam Houldsworth
除了使用案例之外,这个答案今天对我解决一个无关问题很有帮助。 - NappingRabbit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接