我有一组从ConfigurationSection
类继承的类。我还有一个Logger
类,可用于为每个类创建单独的日志文件,但这还不包括这些配置类。
我希望为每个Logger
类创建一个新的ConfigurationSection
实例(因此,创建一个新的日志文件),我希望它们全部使用相同的实例并登录到相同的文件。
实现此目标的最佳方法是什么?
以下是我考虑过的几种选择。
这似乎是最明显的选择,但我真的不喜欢它。我的Logger
类不是完全轻量级的,我希望避免传递这么大的参数。无论如何,这是一个选择。
class Program
{
static void Main( string[] args )
{
// These variables are used for illustrative purposed,
// instead of showing my `Logger' class
bool loggerInstantiated = true;
DateTime InstantiatedTime = DateTime.Now;
DerivedClass01 dc01 = new DerivedClass01( loggerInstantiated, InstantiatedTime );
// added to create delay between the instantiation of different derived classes
System.Threading.Thread.Sleep( 1000 );
DerivedClass02 dc02 = new DerivedClass02( loggerInstantiated, InstantiatedTime );
}
}
class BaseClass : ConfigurationSection
{
public BaseClass( bool loggerInstantiated, DateTime instantiatedAt )
{
Console.WriteLine(string.Format("BaseClass:\n\r\tLoggerInitiated is {0} at {1}\n\r", loggerInstantiated, instantiatedAt ) );
}
}
class DerivedClass01 : BaseClass
{
public DerivedClass01( bool loggerInstantiated, DateTime instantiatedAt ) : base(loggerInstantiated, instantiatedAt)
{
Console.WriteLine( string.Format( "DerivedClass01:\n\r\tLoggerInitiated is {0} at {1}\n\r", loggerInstantiated, instantiatedAt ) );
}
}
class DerivedClass02 : BaseClass
{
public DerivedClass02( bool loggerInstantiated, DateTime instantiatedAt ) : base( loggerInstantiated, instantiatedAt )
{
Console.WriteLine( string.Format( "DerivedClass02:\n\r\tLoggerInitiated is {0} at {1}\n\r", loggerInstantiated, instantiatedAt ) );
}
}
输出:
/*
BaseClass:
LoggerInitiated is True at 21/12/2018 14:16:09
DerivedClass01:
LoggerInitiated is True at 21/12/2018 14:16:09
BaseClass:
LoggerInitiated is True at 21/12/2018 14:16:09
DerivedClass02:
LoggerInitiated is True at 21/12/2018 14:16:09
*/
结论:
这当然是一个选择。从BaseClass
派生的每个类都可以使用相同的Logger
。但是由于我的Logger
类的大小,我认为最好不要将整个实例作为参数传递。
编辑:
正如@Malior和@ScottHannen指出的那样,当您将类的实例作为参数时,我完全误解了实际情况。我现在不想这样做的原因在某种程度上是无效的,但是我仍然宁愿在实例化从Logger
派生的类时也不必传递BaseClass
作为参数。
创建一个继承自BaseClass
的{{1}},并在ConfigurationSection
上实例化Logger
。任何相关的BaseClass
类都可以从此ConfigurationSection
继承。
代码:
BaseClass
输出:
class Program
{
static void Main( string[] args )
{
DerivedClass01 dc01 = new DerivedClass01();
// added to create delay between the instantiation of different derived classes
System.Threading.Thread.Sleep( 1000 );
DerivedClass02 dc02 = new DerivedClass02();
}
}
class BaseClass : ConfigurationSection
{
// These variables are used for illustrative purposed,
// instead of showing my `Logger' class
public bool LoggerInstantiated;
public DateTime InstantiatedAt;
public BaseClass()
{
LoggerInstantiated = true;
InstantiatedAt = DateTime.Now;
Console.WriteLine(string.Format("BaseClass:\n\r\tLoggerInitiated is {0} at {1}\n\r", LoggerInstantiated, InstantiatedAt));
}
}
class DerivedClass01 : BaseClass
{
public DerivedClass01()
{
Console.WriteLine( string.Format( "DerivedClass01:\n\r\tLoggerInitiated is {0} at {1}\n\r", LoggerInstantiated, InstantiatedAt ) );
}
}
class DerivedClass02 : BaseClass
{
public DerivedClass02()
{
Console.WriteLine( string.Format( "DerivedClass02:\n\r\tLoggerInitiated is {0} at {1}\n\r", LoggerInstantiated, InstantiatedAt ) );
}
}
结论:
这不好。每次实例化派生类时都会创建一个新实例/*
BaseClass:
LoggerInitiated is True at 21/12/2018 14:21:51
DerivedClass01:
LoggerInitiated is True at 21/12/2018 14:21:51
BaseClass:
LoggerInitiated is True at 21/12/2018 14:21:52
DerivedClass02:
LoggerInitiated is True at 21/12/2018 14:21:52
*/
(这很明显-我应该早点意识到...)
类似于上面,但是有一个静态实例,而不是实例Logger
。在实例化Logger
之前创建它,并且不要在DerivedClasses
构造函数中创建新实例。
代码:
BaseClass
输出:
class Program
{
static void Main( string[] args )
{
BaseClass.LoggerInstantiated = true;
BaseClass.InstantiatedAt = DateTime.Now;
DerivedClass01 dc01 = new DerivedClass01();
// added to create delay between the instantiation of different derived classes
System.Threading.Thread.Sleep( 1000 );
DerivedClass02 dc02 = new DerivedClass02();
}
}
class BaseClass : ConfigurationSection
{
// These variables are used for illustrative purposed,
// instead of showing my `Logger' class
public static bool LoggerInstantiated;
public static DateTime InstantiatedAt;
public BaseClass()
{
Console.WriteLine(string.Format("BaseClass:\n\r\tLoggerInitiated is {0} at {1}\n\r", LoggerInstantiated, InstantiatedAt));
}
}
class DerivedClass01 : BaseClass
{
public DerivedClass01()
{
Console.WriteLine( string.Format( "DerivedClass01:\n\r\tLoggerInitiated is {0} at {1}\n\r", LoggerInstantiated, InstantiatedAt ) );
}
}
class DerivedClass02 : BaseClass
{
public DerivedClass02()
{
Console.WriteLine( string.Format( "DerivedClass02:\n\r\tLoggerInitiated is {0} at {1}\n\r", LoggerInstantiated, InstantiatedAt ) );
}
}
结论:
这似乎是一个合理的选择。从/*
BaseClass:
LoggerInitiated is True at 21/12/2018 14:05:56
DerivedClass01:
LoggerInitiated is True at 21/12/2018 14:05:56
BaseClass:
LoggerInitiated is True at 21/12/2018 14:05:56
DerivedClass02:
LoggerInitiated is True at 21/12/2018 14:05:56
*/
派生的每个类都可以访问相同的BaseClass
,而不必将其作为参数传递。
答案 0 :(得分:2)
您不必将记录器定义为单例或静态类。这会导致无法测试代码的问题。还有其他方法可以确保所有类都使用相同的实例。
我将从界面开始。编写它,以便它描述您希望类如何使用它,就像这样:
public interface ILogger
{
void Log(string message);
}
然后,将其注入到您的类中,如下所示:
public class YourClass
{
private readonly ILogger _logger;
public YourClass(ILogger logger)
{
_logger = logger;
}
private void MethodThatDoesWhatever()
{
try
{
// do something
}
catch(Exception ex)
{
_logger.Log(ex.ToString());
}
}
}
您尚未编写ILogger
的实现,但这就是重点。如果您的类即使没有实现也可以使用ILogger
,则意味着您的类与ILogger
的实现完全脱钩。它取决于抽象,即依赖反转。
如何确保所有类都使用同一实例?如果您使用的是温莎之类的依赖项注入容器,则可能看起来像这样:
var container = new WindsorContainer();
container.Register(
Component.For<ILogger, YourLoggerImplementation>(),
Component.For<YourClass>(),
Component.For<SomeOtherClassThatNeedsLogger>()
);
当您向容器请求YourClass
的实例时,
var myClass = container.Resolve<YourClass>();
YourClass
的实例。 ILogger
。ILogger
使用的组件(类)是YourLoggerImplementation
,因此它将创建其中一个组件并将其传递给构造函数。YourLoggerImplementation
的新实例。它只会保留相同的实例并重用它,因此现在您所有的类都使用相同的记录器实例。 关于如何使用IoC容器,我已经完全蒙蔽了很多(Windsor,AutoFac,Unity,Ninject,.NET ServiceCollection
,等等。)值得理解,但这超出了此答案的范围。 。要了解更多信息,您需要从所编写的应用程序类型开始。例如,您将搜索“在ASP.NET MVC中使用依赖项注入”或“在WPF中使用依赖项注入”,因为开始的方式与应用程序的种类有关。 (即使那样我还是过分简化了,但是IMO最好以这种方式开始。当您看到一个针对您正在使用的应用程序类型量身定制的示例时,它更有可能被点击。)
然后,您必须编写记录器实现本身。如果它正在写入文件,并且多个类实例都在使用该文件,则建议使用将消息放入ConcurrentQueue
中的实现。然后,当队列达到一定大小或以固定的计时器间隔(或两者都有)时,将消息刷新到文件中。使用ConcurrentQueue
时,多个线程可以同时添加消息,而一个线程可以将消息从队列中取出,并在其他线程写入新消息的同时将它们写入文件。
答案 1 :(得分:0)
我同意@rashmatash。我自己有一个记录器,它是静态的,可以在任何地方使用。它可以创建新文件,也可以追加到现有文件中。
public方法接受并例外,可能是自定义消息,还可能是所需日志文件的完整路径的字符串。
public static void LogError(Exception Ex, string Message = null, string LogFile = null)
如果LogFile保留为空,则将在应用程序基本目录+ \ Logs中创建/附加一个名为ErrorLog.txt的日志文件。
这是主要部分:
if (LogFile == null || LogFile == String.Empty)
{
LogFile = AppDomain.CurrentDomain.BaseDirectory + @"\Logs\ErrorLog.txt";
}
string logDir = Path.GetDirectoryName(LogFile);
int lineNo = GetExceptionLineNumber(Ex);
string message = CreateMessageString(Ex, lineNo, Message);
try
{
if (VerifyOrCreateDirectory(logDir) == true)
{
StreamWriter streamWriter = new StreamWriter(path: LogFile, append: true);
string logEntryHeaderInfo = DateTime.Now + " :: " + AppDomain.CurrentDomain.FriendlyName.ToString() + " :: " + Environment.UserName + " :: " + Environment.MachineName;
streamWriter.WriteLine(logEntryHeaderInfo + " :: " + message);
streamWriter.Flush();
streamWriter.Close();
}
}
答案 2 :(得分:0)
您可以使用单例模式
collectstatic
因此,您可以在任何部分或代码中使用同一实例:
class Attribute:
def __init__(self, pos_x, pos_y):
self.attr_top = Toplevel()
print(root.winfo_x() + pos_x)
self.attr_top.geometry('+' + str(root.winfo_x() + pos_x) + '+' + str(root.winfo_y() + pos_y))
self.name_var = StringVar()
self.name_var.set('name')
self.name_label = Label(self.attr_top, textvariable = self.name_var)
self.name_label.grid(row = 0, column = 0)
self.name_enter = Entry(self.attr_top)
self.name_enter.grid(row = 0, column = 1)
self.next_name_var = StringVar()
self.next_name_var.set('link')
self.next_name_label = Label(self.attr_top, textvariable = self.next_name_var)
self.next_name_label.grid(row = 1, column = 0)
self.next_name_enter = Entry(self.attr_top)
self.finish_button = Button(self.attr_top, text = 'finsh edit', command = self.button_close_top)
self.finish_button.grid(row = 3, column = 0, columnspan = 2)
self.attr_top.bind('<Return>', self.return_close_top)
self.next_name_enter.grid(row = 1, column = 1)
def button_close_top(self):
self.name = self.name_enter.get()
self.next_name = self.next_name_enter.get()
self.attr_top.destroy()
def return_close_top(self, event):
self.button_close_top()
def save_attr(self, event):
name = self.attr.name
next_name = self.attr.next_name
print('jinru')
name_font = tkfont.Font(size = 10)
self.enter_text.insert(END, name)
self.enter_text.tag_add(name, 1.0, END)
self.enter_text.tag_config(name, font = name_font, offset = 2, underline = 1)
def edit_brief(self):
print('edit brief')
self.attr = Attribute(self.widget_pos_x, self.widget_pos_y)
self.enter_text = Text(self.window_frame, width = 30, height = 3)
self.enter_text.place(x = self.widget_pos_x, y = self.widget_pos_y)
self.attr.attr_top.bind('<Destroy>', self.save_attr)
或者也许您想使用像Ninject,Unity,Simple Injector这样的依赖注入框架