class LogUtil : ILogUtility
{
object _classtype;
log4net.ILog log;
public LogUtil(object classtype)
{
_classtype = classtype;
log = log4net.LogManager.GetLogger(_classtype.GetType().FullName);
}
public void Log(LogType logtype, string message)
{
Console.WriteLine("logging coming from class {0} - message {1} " , _classtype.GetType().FullName, message);
}
}
From the client class code I call the above LogUtil class as follows:
public class TestCode
{
public void test()
{
LogUtil logutil = new LogUtil(this);
}
}
In the LogUtil constructor, I want to AVOID passing in object classtype.
I want to pass in the client class object that I can pass to the GetLogger Method. GetLogger method needs to know which class instantiated the LogUtil class.
The way I have it now, I can pass in integer variable class type and it would work. I want to avoid it.
How do I do that? How can I utilize generics method in this case?
Is this what you want?
class LogUtil<T> : ILogUtility
{
log4net.ILog log;
public LogUtil()
{
log = log4net.LogManager.GetLogger(typeof(T).FullName);
}
public void Log(LogType logtype, string message)
{
Console.WriteLine("logging coming from class {0} - message {1}", typeof(T).FullName, message);
}
}
You could then use it like this:
LogUtil<TestCode> logutil = new LogUtil<TestCode>();
If you use method level generics, rather than class level generics (or rather, in addition to in this case), you can let type inference make things easier on you.
class LogUtil<T> : ILogUtility
{
log4net.ILog log;
public LogUtil<T>()
{
log = log4net.LogManager.GetLogger(typeof(T));
}
public void Log(LogType logtype, string message)
{
Console.WriteLine("logging coming from class {0} - message {1} " , typeof(T).FullName, message);
}
public static LogUtil<NewType> Create<NewType>(NewType instance)
{
return new LogUtil<NewType>();
}
}
public class TestCode
{
public void test()
{
var logutil = LogUtil.Create(this);
}
}
I'd change it to this:
class LogUtility : ILogUtility
{
private Type classType;
public LogUtil(object o)
: this(o.GetType())
{
}
public LogUtil(Type t)
{
this.classType = t;
}
public static LogUtil Create<T>()
{
return new LogUtil(typeof(T));
}
public void Log(string message)
{
Console.WriteLine("logging coming from class {0} - message {1} " , this.classType, message);
}
}
Please modify the LogUtil class as below.
class LogUtil : ILogUtility
{
Type _classtype;
log4net.ILog log;
public LogUtil(Type classtype)
{
_classtype = classtype;
log = log4net.LogManager.GetLogger(_classtype.FullName);
}
public void Log(LogType logtype, string message)
{
Console.WriteLine("logging coming from class {0} - message {1} " , _classtype.FullName, message);
}
}
The client code should be as below,
public class TestCode
{
public void test()
{
LogUtil logutil = new LogUtil(GetType());
}
}
Related
In a WinForms app, there is Logger class that is a form designed for logging, so that any class can call it.
There is a static Configuration class, inside which a Logger lives.
Previous implementation
Various classes would call the logger like so:
public class ImportController
{
public void import()
{
try
{
// do the work...
}
catch (Exception ex)
{
Configuration.logger.log("Something failed");
Configuration.logger.log(ex);
}
}
}
Current implementation
The logger implements the following interface, which was extracted from it as part of refactoring to enable unit testing calling classes through dependency injection:
public interface ILogger
{
void (string message, [CallerMemberName] string member = "", [CallerLineNumberAttribute] int lineNumber = -1, string fileName = "");
void (Exception ex, [CallerMemberName] string member = "", [CallerLineNumberAttribute] int lineNumber = -1, string fileName = "");
}
As can be seen, the idea is to have it automatically log the calling class name and source file path.
The following is an example of an attempt to inject a logger into all classes that use it, in this instance the ImportController from above:
public class ImportControllerLogger
{
public void log(string message, [CallerMemberName] string member = "", [CallerLineNumber] int line_num = -1, string filename = "")
{
Configuration.log.log(string message, "ImportController", lineNumber, #"Controllers\ImportController.cs");
}
public void log(Exception exception, [CallerMemberName] string member = "", [CallerLineNumber] int line_num = -1, string filename = "")
{
Configuration.log.log(exception, "ImportController", lineNumber, #"Controllers\ImportController.cs");
}
}
public class ImportController
{
ILogger _logger;
public ImportController(ILogger logger)
{
this._logger = logger;
}
public void import()
{
try
{
// do the work...
}
catch (Exception ex)
{
_logger.log("Something failed");
_logger.log(ex);
}
}
}
Questions
Is this the correct approach to decouple the logger from all classes that use it?
It seems it might be better to create a single "LoggerHelper" class, that abstracts away the logger so that any class can make a call to it, instead of creating such a class for every calling class. How can the name of the calling class and source file path for the calling class be logged, in a proper way, without resorting to manually specifying it for each class? It worked in the previous implementation with the attributes.
I also had to implement something like that.
The code is simplified.
ILogger
public interface ILogger
{
event EventHandler<LogEventArgs> OnLogAdded;
Type Type { get; }
void Log(string message);
}
Logger
public class Logger : ILogger
{
public Type Type { get; }
public Logger(Type type)
{
Type = type;
}
public event EventHandler<LogEventArgs> OnLogAdded;
public void Log(string message)
{
EventHandler<LogEventArgs> handler = OnLogAdded;
handler?.Invoke(this, new LogEventArgs(message));
}
}
LogProvider
public static class LogProvider
{
private static List<ILogger> loggers = new List<ILogger>();
public static ILogger CreateLogger<T>()
{
if (loggers.Select(x => x.Type.Equals(typeof(T))).Count() > 0)
{
throw new Exception($"There is allready a logger for the type {typeof(T)}");
}
ILogger logger = new Logger(typeof(T));
logger.OnLogAdded += OnLogAdded;
loggers.Add(logger);
return logger;
}
private static void OnLogAdded(object sender, LogEventArgs e)
{
//add log to your config
}
}
And you can use it like this:
public class SampleView
{
private ILogger logger = LogProvider.CreateLogger<SampleView>();
public SampleView()
{
logger.Log("TestLog");
}
}
I don't know if this is the best implementation, but it works like a charm.
Autofac and EasyNetQ are almost all the time easy to work with but today we have kind of challenge: i want to set up unit of work for subscribers
class Program
{
static void Main(string[] args)
{
var container = AutofacContainerFactory.Build();
using (var scope = container.BeginLifetimeScope())
{
var bus = scope.Resolve<IBus>();
bus.Subscribe<SomeMessage>("some.queue", container.Resolve<ISomeMessageHandler>().Handle);
Console.WriteLine("Listening for messages. Hit <return> to quit.");
Console.ReadLine();
}
}
}
This is fairly easy subscriber in console app but how to properly setup UoW: i would be nice if some interceptor was called before handler Handle method was called and UoW was called after execution. Another resolution I can think of is do everything inside Handle method: maybe using some kind of UnitOfWork decorator pattern. I waiting for your thoughts.
I've created a AutofacMessageDispatcher that can do all that:
public class AutofacMessageDispatcher : IAutoSubscriberMessageDispatcher
{
readonly ILifetimeScope _component;
readonly IMessageContextFactory _contextFactory;
public const string PerMessageLifeTimeScopeTag = "AutofacMessageScope";
public const string GlobalPipeTag = "global";
public AutofacMessageDispatcher(ILifetimeScope component, IMessageContextFactory contextFactory)
{
_component = component;
_contextFactory = contextFactory;
}
static IEnumerable<IErrorHandler> GetErrorHandlers<TConsumer>(TConsumer consumer, IComponentContext scope)
{
var errorHandlers = consumer.GetType()
.GetTypeInfo().GetAttributes<ErrorHandlerAttribute>()
.OrderBy(attribute => attribute.Order)
.Select(attribute => attribute.Initialize((IErrorHandler) scope.Resolve(attribute.ErrorHandlerType)))
.Union(scope.ResolveNamed<IEnumerable<IErrorHandler>>(GlobalPipeTag), a => a.GetType()); // perform the distinction in the union on GetType so we only get 1 handler of the same type
if (consumer is IErrorHandler consumerAsErrorHandler)
errorHandlers = errorHandlers.Concat(new[] { consumerAsErrorHandler });
return errorHandlers;
}
static IEnumerable<IPipe> GetPipeLine<TConsumer>(TConsumer consumer, IComponentContext scope)
{
var pipeLine = consumer.GetType()
.GetTypeInfo().GetAttributes<PipeAttribute>()
.OrderBy(attribute => attribute.Order)
.Select(attribute => attribute.Initialize((IPipe) scope.Resolve(attribute.PipeType)))
.Union(scope.ResolveNamed<IEnumerable<IPipe>>(GlobalPipeTag), a => a.GetType()); // perform the distinction in the union on GetType so we only get 1 handler of the same type
return pipeLine;
}
[HandleProcessCorruptedStateExceptions]
public void Dispatch<TMessage, TConsumer>(TMessage message)
where TMessage : class
where TConsumer : IConsume<TMessage>
{
using (var scope = _component.BeginLifetimeScope(PerMessageLifeTimeScopeTag, _contextFactory.RegisterMessageContext(typeof(TConsumer), message)))
{
var consumer = scope.Resolve<TConsumer>();
var pipeLine = GetPipeLine(consumer, scope).ToArray();
pipeLine.Each(p => p.OnBeforeConsume(consumer, message));
Exception exception = null;
try
{
consumer.Consume(message);
}
catch (Exception e) when (GetErrorHandlers(consumer, scope).Any(p => p.OnError(consumer, message, e)))
{
exception = e;
}
pipeLine.Reverse().Each(p => p.OnAfterConsume(consumer, message, exception));
}
}
[HandleProcessCorruptedStateExceptions]
public async Task DispatchAsync<TMessage, TConsumer>(TMessage message)
where TMessage : class
where TConsumer : IConsumeAsync<TMessage>
{
using (var scope = _component.BeginLifetimeScope(PerMessageLifeTimeScopeTag, _contextFactory.RegisterMessageContext(typeof(TConsumer), message)))
{
var consumer = scope.Resolve<TConsumer>();
var pipes = GetPipeLine(consumer, scope).ToArray();
Exception exception = null;
foreach (var hook in pipes)
await hook.OnBeforeConsumeAsync(consumer, message);
try
{
await consumer.Consume(message);
}
catch (Exception e) when (GetErrorHandlers(consumer, scope).Any(p => p.OnErrorAsync(consumer, message, e)))
{
exception = e;
}
foreach (var hook in pipes.Reverse())
await hook.OnAfterConsumeAsync(consumer, message, exception);
}
}
}
public interface IMessageContextFactory
{
Action<ContainerBuilder> RegisterMessageContext<TMessage>(Type consumerType, TMessage message) where TMessage : class;
}
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class ErrorHandlerAttribute : Attribute
{
public ErrorHandlerAttribute(Type errorHandlerType, int order = 0)
{
ErrorHandlerType = errorHandlerType;
Order = order;
}
public Type ErrorHandlerType { get; set; }
public int Order { get; set; }
public virtual IErrorHandler Initialize(IErrorHandler handler)
{
return handler;
}
}
public interface IErrorHandler
{
bool OnError<TMessage, TConsumer>(TConsumer consumer, TMessage message, Exception exception)
where TMessage : class
where TConsumer : IConsume<TMessage>;
bool OnErrorAsync<TMessage, TConsumer>(TConsumer consumer, TMessage message, Exception exception)
where TMessage : class
where TConsumer : IConsumeAsync<TMessage>;
}
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class PipeAttribute : Attribute
{
public PipeAttribute(Type pipeType, int order = 0)
{
PipeType = pipeType;
Order = order;
}
public Type PipeType { get; set; }
public int Order { get; set; }
public IPipe Initialize(IPipe pipe)
{
return pipe;
}
}
public interface IPipe
{
void OnBeforeConsume<TMessage, TConsumer>(TConsumer consumer, TMessage message)
where TMessage : class
where TConsumer : IConsume<TMessage>;
void OnAfterConsume<TMessage, TConsumer>(TConsumer consumer, TMessage message, [CanBeNull] Exception exception)
where TMessage : class
where TConsumer : IConsume<TMessage>;
Task OnBeforeConsumeAsync<TMessage, TConsumer>(TConsumer consumer, TMessage message)
where TMessage : class
where TConsumer : IConsumeAsync<TMessage>;
Task OnAfterConsumeAsync<TMessage, TConsumer>(TConsumer consumer, TMessage message, [CanBeNull] Exception exception)
where TMessage : class
where TConsumer : IConsumeAsync<TMessage>;
}
public interface IMessageContext
{
object Message { get; }
}
public class MessageContext : IMessageContext
{
public MessageContext(object message)
{
Message = message;
}
public object Message { get; set; }
}
public class MessageContextFactory : IMessageContextFactory
{
readonly ILogger _logger;
public MessageContextFactory()
{
_logger = logger;
}
public Action<ContainerBuilder> RegisterMessageContext<TMessage>(Type consumerType, TMessage message) where TMessage : class
{
return builder =>
{
builder.RegisterInstance(new MessageContext(message)).As<IMessageContext>().AsSelf();
var forContext = _logger.ForContext(message.GetType());
builder.RegisterInstance(forContext).As<ILogger>().AsSelf();
};
}
}
public interface IMessageContextFactory
{
Action<ContainerBuilder> RegisterMessageContext<TMessage>(Type consumerType, TMessage message) where TMessage : class;
}
This allows you to:
Register custom 'pipes' and 'errorhandlers' using attributes on your consumers
You can register pipes and error handlers globally on in your container if you register them with the 'global' name
You can resolve 'IMessageContext' anywhere in your dependencies to get access to the message
if you use serilog, it will create a logging context including your message (otherwise just get rid of the ILogger references)
Sorry I just added the code quickly I might have missed some dependencies.
Here's a repo where I keep my EasyNetQ extensions if you miss anything:
https://github.com/zidad/net-tools/tree/master/src/Net.EasyNetQ
Hope this helps!
public class UnitOfWorkDecorator<TRequest, THandler> : IHandler<TRequest>
where TRequest : class
where THandler : IHandler<TRequest>
{
protected readonly Func<ILifetimeScope> ParentScope;
public UnitOfWorkDecorator(Func<ILifetimeScope> parentScope)
{
ParentScope = parentScope;
}
public void Handle(TRequest request)
{
Console.WriteLine("UoW handler start");
using (var scope = ParentScope().BeginLifetimeScope())
{
var scopedHandler = scope.Resolve<THandler>();
scopedHandler.Handle(request);
}
Console.WriteLine("UoW handler end");
}
}
Test
[Test]
public void UnitOfWork()
{
var builder = new ContainerBuilder();
builder.RegisterType<MessageCommandHandler>().As<IHandler<Message>>().InstancePerLifetimeScope();
builder.RegisterType<MessageCommandHandler2>().As<IHandler<Message>>().InstancePerLifetimeScope();
builder.RegisterGeneric(typeof(UnitOfWorkDecorator<,>)).AsSelf().SingleInstance();
var container = builder.Build();
var handler = container.Resolve<IHandler<Message>>();
var uow = container.Resolve<UnitOfWorkDecorator<Message, IHandler<Message>>>();
uow.Handle(new Message());
}
so the main idea is to create nested scope for resolving decorated handler.
Logic for managing unit of work can be resolved inside scope so for example decorated handler and uow can have same instance of EF db context.
I have multiple classes that inherit from IPrint. I want to instantiate these classes using a factory but I want to maintain a single instance of each type.
Is this possible?
Please find my sample code below.
public interface IPrint
{
void DoPrint();
}
public class DigitalPrint : IPrint
{
public void DoPrint()
{
// logic
}
}
public class InkPrint : IPrint
{
public void DoPrint()
{
// logic
}
}
public class PrintFactory
{
private static IDictionary<IPrint, object> prints = new
Dictionary<IPrint, object>();
private PrintFactory()
{
}
public static IPrint GetPrint(PrintType type)
{
// return instance depending on type. Instantiate only once
//like singleton
// How to write so that it returns a single instance depending
//on type
return null;
}
}
public enum PrintType
{
DigitalPrint,
InkPrint
}
Can someone give me some idea if this is possible?
Thanks.
You can create IPrint instances when initializing Dictionary<PrintType, IPrint>:
private static IDictionary<PrintType, IPrint> prints =
new Dictionary<PrintType, IPrint> {
{ PrintType.DigitalPrint, new DigitalPrint() },
{ PrintType.InkPrint, new InkPrint() }
};
Getting print (thus print is a class, the same instance will be returned for each request):
public static IPrint GetPrint(PrintType type)
{
IPrint print;
if (!prints.TryGetValue(type, out print))
return null;
return print;
}
If you don't want to create IPrint instances until the client asks for them, you can use Dictionary<PrintType, Lazy<IPrint>>:
private static IDictionary<string, Lazy<IPrint>> prints =
new Dictionary<string, Lazy<IPrint>> {
{ PrintType.DigitalPrint, new Lazy<IPrint>(() => new DigitalPrint()) },
{ PrintType.InkPrint, new Lazy<IPrint>(() => new InkPrint()) }
};
Getting print (in this case only one instance of each IPrint type will be created, but not before someone tries to get the instance of that type):
public static IPrint GetPrint(PrintType type)
{
Lazy<IPrint> factory;
if (!prints.TryGetValue(type, out factory))
return null;
return factory.Value;
}
Though I would consider using dependency injection framework instead of implementing such functionality manually.
Further reading: Ninject or Autofac
Yes, it's possible.
This creates the IPrint's before they're needed. You could make them lazily created, instead.
public class Program
{
public static void Main(string[] args)
{
var factory = new PrintFactory();
Console.WriteLine(PrintFactory.GetPrint(PrintType.DigitalPrint));
Console.WriteLine(PrintFactory.GetPrint(PrintType.InkPrint));
}
}
public interface IPrint
{
void DoPrint();
}
public class DigitalPrint : IPrint
{
public void DoPrint()
{
// logic
}
}
public class InkPrint : IPrint
{
public void DoPrint()
{
// logic
}
}
public class PrintFactory
{
// Make the dictionary from PrintType to IPrint instead of IPrint to object
private static IDictionary<PrintType, IPrint> prints = new Dictionary<PrintType, IPrint>();
// Initialize prints in a static constructor.
static PrintFactory()
{
prints.Add(PrintType.DigitalPrint, new DigitalPrint());
prints.Add(PrintType.InkPrint, new InkPrint());
}
public static IPrint GetPrint(PrintType type)
{
if (!prints.ContainsKey(type))
{
// TODO: Maybe throw an exception or log?
}
return prints[type];
}
}
public enum PrintType
{
DigitalPrint,
InkPrint
}
I would get rid of the enum and make a generic method:
public static IPrint GetPrint<T>() where T : IPrint, new ()
{
foreach (var key in prints.Keys) {
if (key is T)
return null;
}
return new T();
}
I am working on a small project, I am trying to create a layer that will handle exception and logging.
This layer will sit in between User interface and DAL, mostly like BAL which will have some generic method that will then initiate further call to Data access layer.
Some thing like this
Public Class ExceptionHandler
{
//which should take a method name,parameters and return a object.
Public T InitiateDatabaseCall(//method name as input,parameters)
{
try
{
//then make the call to the method using the input parameter and pass the parameters
}
catch(Exception e)
{
// do logging
}
}
This layer will act as center repository to handle and log exception. I am not able to create the method that i described can experts provide some snippet that will show case this scenario.
Edited: With code added
static void Main(string[] args)
{
BAL b = new BAL();
var ll = b.GetFieldList("xxxxyyyy");
}
public class BAL
{
public List<Fields> GetFieldList(string screen)
{
if(!string.IsNullOrEmpty(screen))
{
ExceptionHandler.InitiateCall(() =>GetList(screen) ));
}
}
}
public static class ExceptionHandler
{
public T InitiateCall<T>(Func<T>method,object[] parms) where T : object
{
try
{
return method.Invoke();
}
catch(Exception ex)
{
return default(T);
}
}
}
public class DAL
{
public List<Fields> GetList(string name)
{
VipreDBDevEntities context = new VipreDBDevEntities();
return context.Database.SqlQuery<Fields>("SCREEN_FIELDS_SELECT #SCREEN_NAME", name).ToList();
}
}
It gives error GetList() does not exist in current context.
For these kind of things AOP (Aspect Oriented Programming, see https://en.wikipedia.org/wiki/Aspect-oriented_programming) is a really good fit.
These are cross-cutting concerns that are cluttering code if not done properly.
See for an example AOP framework PostSharp. Even with the free version that is simple to code. There are also (might be paid) build-in aspects for that, like http://doc.postsharp.net/exception-tracing.
A simple alternative is using a Func or Action (try it out in a Console App):
static void Main(string[] args)
{
ExceptionHandler.InitiateDatabaseCall(() => CallDb("Dummy"));
ExceptionHandler.InitiateDatabaseCall<int>(() => { throw new InvalidOperationException(); });
}
int CallDb(string justToShowExampleWithParameters)
{
return 5;
}
public static class ExceptionHandler
{
public static T InitiateDatabaseCall<T>(Func<T> method)
{
try
{
return method.Invoke();
}
catch (Exception e)
{
// do logging
Console.WriteLine(e.Message);
return default(T); // or `throw` to pass the exception to the caller
}
}
}
Edit:
based on your added code in the question you can solve the error about GetList() by some minor modifications:
static void Main(string[] args) {
BAL b = new BAL();
var ll = b.GetFieldList("xxxxyyyy");
}
public class BAL
{
public List<Fields> GetFieldList(string screen)
{
if (!string.IsNullOrEmpty(screen))
{
return ExceptionHandler.InitiateCall(() => new DAL().GetList(screen)); // Slight modification of your code here
}
else
{
return null; // or whatever fits your needs
}
}
}
public class ExceptionHandler
{
public static T InitiateCall<T>(Func<T> method)
{
try
{
return method.Invoke();
}
catch (Exception ex)
{
//log
return default(T);
}
}
}
public class DAL
{
public List<Fields> GetList(string name)
{
VipreDBDevEntities context = new VipreDBDevEntities();
return context.Database.SqlQuery<Fields>("SCREEN_FIELDS_SELECT #SCREEN_NAME", name).ToList();
}
}
You don't need the object[] parms parameter in InitiateCall given the provided code. Any paramaters you need for the method call are given in the Func<T>
Personally, I think loging should be done in two ways:
Step logging (when you log some step in your code)
Scope logging (when you log begin/end of some code or it's time)
So, I always choose to create ILogger class with these two ways:
public sealed class Logger : ILogger
{
private readonly Serilog.ILogger _seriLogger;
public Logger(Serilog.ILogger seriLogger)
{
_seriLogger = seriLogger;
}
public void Debug(string format, params object[] args)
{
_seriLogger.Debug(format, args);
}
public void Info(string format, params object[] args)
{
_seriLogger.Information(format, args);
}
public void Warn(string format, params object[] args)
{
_seriLogger.Warning(format, args);
}
public void Error(Exception e, string format, params object[] args)
{
_seriLogger.Error(e, format, args);
}
public void Fatal(Exception e, string format, params object[] args)
{
_seriLogger.Fatal(e, format, args);
}
public IDisposable GetScope(string name, long timeout = 0)
{
return new LoggerScope(this, name, timeout);
}
}
internal class LoggerScope : IDisposable
{
private readonly ILogger _logger;
private readonly string _name;
private readonly long _timeout;
private readonly Stopwatch _sw;
private bool ExceedScope
{
get { return _timeout > 0; }
}
public LoggerScope(ILogger logger, string name, long timeout)
{
_logger = logger;
_name = name;
_timeout = timeout;
if (!ExceedScope)
{
_logger.Debug("Start execution of {0}.", name);
}
_sw = Stopwatch.StartNew();
}
public void Dispose()
{
_sw.Stop();
if (ExceedScope)
{
if (_sw.ElapsedMilliseconds >= (long)_timeout)
{
_logger.Debug("Exceeded execution of {0}. Expected: {1}ms; Actual: {2}ms.", _name, _timeout.ToString("N"), _sw.Elapsed.TotalMilliseconds.ToString("N"));
}
}
else
{
_logger.Debug("Finish execution of {0}. Elapsed: {1}ms", _name, _sw.Elapsed.TotalMilliseconds.ToString("N"));
}
}
}
Then, If I want to log something, I use it like this, without AOP:
using(_log.GetScope("Some describable name"))
{
//Some code here
}
Why doesn't the line marked with //Dont work in the bottom of the code compile?
I want to reuse the WriteMessage method with different Classes, I try to use generics, but I'm not sure how to use it.
class ClassOne
{
public string MethodOne()
{
return ("ClassOne");
}
public string MethodTwo()
{
return ("ClassOne -MethodTwo ");
}
}
class ClassTwo
{
public string MethodOne()
{
return ("ClassTwo");
}
public string MethodTwo()
{
return ("ClassOne -MethodTwo ");
}
}
class Program
{
private static void Main()
{
var objectOne = new ClassOne();
WriteMessage(objectOne);
var objectTwo = new ClassTwo();
WriteMessage(objectTwo);
Console.ReadKey();
}
public static void WriteMessage<T>(T objectA)
{
var text = objectA.MethodTwo(); //Dont Work
Console.WriteLine("Text:{0}", text);
}
}
Try implementing a interface :
Example :
public interface IHasTwoMethods
{
string MethodOne()
string MethodTwo()
}
Implement this inteface on your classes :
class ClassOne : IHasTwoMethods
class ClassTwo : IHasTwoMethods
Then in your generic method do like this :
public static void WriteMessage<T>(T objectA) where T : IHasTwoMethods
{
var text = objectA.MethodTwo(); //Will work
Console.WriteLine("Text:{0}", text);
}
You can read more about interfaces here : http://msdn.microsoft.com/en-us/library/87d83y5b.aspx
This doesn't compile because as far as the compiler is concerned objectA is just an Object.
To get this to work, you need to use a generic type constraint:
public interface MyInterface
{
string MethodTwo();
}
public class A : MyInterface
{
...
}
public class B : MyInterface
{
...
}
public static void WriteMessage<T>(T objectA) where T: MyInterface
{
var text = objectA.MethodTwo(); //Will Work!
Console.WriteLine("Text:{0}", text);
}
MSDN : Constraints on Type Parameters
Since you're passing in a generically-typed object with T, the compiler doesn't know what class you're using--for all it knows, it could be an int or an Application or anything.
What you probably want is to have ClassOne and ClassTwo inherit from another class that has an abstract MethodTwo class that both implement. Something like...
abstract class SuperClass
{
public abstract string MethodOne();
}
class ClassOne : SuperClass
{
public override string MethodOne()
{
return ("ClassOne");
}
}
then in Main:
public static void WriteMessage<T>(T objectA) where T : SuperClass
{
var text = objectA.MethodOne();
Console.WriteLine("Text:{0}", text);
}
Read up on C# inheritance here: http://msdn.microsoft.com/en-us/library/ms173149.aspx