I have a bit of confusion with this C# syntax.
I'm trying to assign an arbitrary delegate to a class.
I have a delegates defined as
delegate string stringCB(string s);
delegate int intCB(int i);
I have a class
class run_by_name {
public string name {get;set;}
public Delegate method {get;set;}
};
And I'm trying to instantiate it
run_by_name myfuc = new run_by_name(){
name = "my name",
method = new stringCB(string s) {
return " testing " + s;
};
};
I'm really not clear how to assign to a delegate when there's a return type. Also I'm not sure how to call that method later on syntactically.
Why I'm doing this? Well I'm just writing some code that follows a pattern I use in JS a lot for event handling, I'm just making an "object" I can assign arbitrary functions to for a generic event handler that's created rather than defined. (important)
Also, alternatives to using delegates are welcome. :)
EDIT: How I might use it later
I don't have that written yet but Im pretty sure I'll be doing this.
List<run_by_name> callbacks = new List<run_by_name>();
/* lets say this is initialized and filled at this point */
public object FindAndRunCallback(string Name, object input) {
foreach(var cb in callbacks) {
if( cb.name == Name )
return cb.method(input);
}
return null;
}
Here's the syntax you need to get your current code working:
method = new stringCB((string s) => {
return " testing " + s;
})
Or, using lambda expressions:
method = new stringCB(s =>" testing " + s)
You could later invoke the method like so:
string result = (string) myfuc.method.DynamicInvoke("hello");
Without knowing more about your use case, it's hard to recommend other approaches, but I'd recommend at least looking into the following:
Events: Your description sounds very close to the common pattern for events and event handlers in C#. Your event defines the delegate type that is used to handle it, and code elsewhere can subscribe to that event with methods that match that delegate type. By convention, people usually pass a sender object as the first parameter, and some strongly-typed EventArgs so that subscribers don't have to guess at what data is going to be available when the event fires.
Func<> and Action<> variants: As C# has evolved into a more functional language, programmers have trended away from using custom delegate types, and toward using the provided variants of Func<> and Action<>. Since the arguments are strongly-typed still, you get most of the advantages of a compiled language, but use a little "duck typing" for the actual function you pass around.
For example, you could give your class a generic type based on what types you expect your delegate to deal with:
class run_by_name<T> {
public string name {get;set;}
public Func<T, T> method {get;set;}
};
Then use it:
var myfuc = new run_by_name<string>{
name = "my name",
method = s =>" testing " + s
};
string result = myfuc.method("hello");
dynamic: This keyword allows you to late-bind actions on any object. You lose the advantages of a compiled language, but it improves interoperability with more dynamic languages immensely. For example, an object can be created via JSON, and you can access the properties on it without declaring a special type for that object, just like you can in Javascript.
For example, if you changed your method declaration to this:
public dynamic method {get;set;}
Then you could simply say:
string result = myfuc.method("hello");
You have seceral choices. the strinCB constructor expects a method, that takes a string parameter and returns a string. If you have an existing method, you can pass it's name to the constructor, or you can create an anonymous method wither by delegate syntax like this:
method = new stringCB(delegate(string s)
{
return " testing " + s;
})
Or using a lambda expression:
method = new stringCB(s =>
{
return " testing " + s;
})
Then you can call it like this: myfuc.method.DynamicInvoke(YourParameter);
Normally, calling a delegate's method is pretty easy like this:
Func<int, int, int> sum = (x, y) => x + y;
Console.WriteLine(sum(2,3)); // with the name of delegate
Console.WriteLine(sum.Invoke(2,3)); // or using Invoke method
But in this case, you need to use DynamicInvoke because the type of your property is Delegate.
Apart from that .NET Framework has some useful built-in delegate types, such as Func,Action and Predicate etc. You can use them instead of creating your own delegate as long as they satisfy your needs. For example in this case you can use a Func<string,string> instead of stringCB.
Related
I have to convert the following expression from VB.NET to C#:
Dim query = (From egyosztaly As UpdateItemBase In UpdateItemCollectionRemote
Group Join egystudent As UpdateItemBase In UpdateItemCollectionLocal On egyosztaly.HashCode Equals egystudent.HashCode
Into csoport = Group
From egycsoportelem In csoport.DefaultIfEmpty
Select New ComparedUpdateItem With {.Remote = egyosztaly, .Local = egycsoportelem}).ToList()
My attempt:
var query = (from egyosztaly in UpdateItemCollectionRemote
join egystudent in UpdateItemCollectionLocal on egyosztaly.HashCode equals egystudent.HashCode into csoport
from egycsoportelem in csoport.DefaultIfEmpty
select new ComparedUpdateItem() { Remote = egyosztaly, Local = egycsoportelem }).ToList();
The second "from" part is not understandable by C#.
Thanks for help!
In C# you need to be explicit when you call a method with no arguments, to provide empty parentheses. Identifiers without parens are usually considered properties, not methods (functions in vb nomenclature)
var query = (
from egyosztaly in UpdateItemCollectionRemote
join egystudent in UpdateItemCollectionLocal on egyosztaly.HashCode equals egystudent.HashCode into csoport
from egycsoportelem in csoport.DefaultIfEmpty()
select new ComparedUpdateItem() {
Remote = egyosztaly,
Local = egycsoportelem
}
).ToList();
DefaultIfEmpty is a method and you want to call it, so you need to add ()
In VB developers often don't really care about the same set of things we care about in C#; in C# we care quite a lot about things like naming and case sens:
class SomeName{ //class names PascalCase
private int _someInt; //private vars camelCase, usually underscore led
public int SomeInt { get { return _someInt; } } //public properties PascalCase
public void SomeMethod(int someArgument){ //methods PascalCase, arguments camelCase
void localFunction() { ... }; //local function, usually camelCase
int localVariable = 0; //local vars camelCase
SomeStaticClass.SomeMethodWithNoArgs(); //Static classes PascalCase, method calls with no args must include ()
someInstance.SomeMethod(1);
var x = new Thing { X = 1 }; //new calling default constructor no longer requires () if initializing properties
Thing x = new(); //but does if new stands alone
}
}
Ultimately, it's usually possible to look at some code and know what is going on just from the casing; the only time it gets hazy is in scenarios like this:
AddAction(Foo.SomeThing) //AddAction is a method that takes.. er..
Foo.SomeThing here could be a property of an instance called Foo or it could be a method of a type called Foo - when we're calling a method that takes a method (a delegate) as a parameter we pass the method name without the () - putting () would call the method and pass the result
AddAction(Foo.SomeThing) //SomeThing could be a property or a method
AddAction(Foo.SomeThing()) //SomeThing is a method, called and its return value passed to AddAction
In latest VS properties and methods are colored differently which helps with this case. For a use case where you have "a method that takes a method", you'll come across that quite a lot in LINQ, but I'll give an example using List:
var strings = new List<string>() {"hello","world"};
strings.ForEach(Console.WriteLine);
ForEach is a method that takes a method argument. List will loop over itself calling the supplied method once for every item. The method that is passed must accept a string as an argument, like Console.WriteLine does. All the strings in the list will print to the console. It's legal to pass the method name without the () because we don't want to call the WriteLine method and pass the result to ForEach (actually WriteLine has no result); we want to pass the WriteLine method itself to ForEach
Similarly this syntax "creates a mini method":
x => something_here;
So this is legal too:
strings.ForEach(x => Console.WriteLine(x.ToUpper()));
The whole x => Console.WriteLine(x.ToUpper()) thing is "a method that takes a parameter called x, of type that will be deduced from ForEach's signature which for a list of strings is 'a method that takes a single string argument'". In VB the equivalent is:
strings.ForEach(Function(x) Console.WriteLine(x.ToUpper));
(And of course VB wouldnt care if if was ToUpper or ToUpper() but C# people care quite a bit about these detils because it's vital to help understand a program and get clues as to what is what) :)
I'm a bit confused atm and i dont think it could be this hard as then i must be doing something wrong. What i am trying to do now for the past 2 days is to access a method inside an object that is stored in an List and i just cant get at it. In my mind it should just be to get the object back to its originated type and invoke the method but i just cant do it.
I been reading alot about Type, Generics and reflection but it cant get anyting to work so i am obviusly doing it all wrong and i need help finding the light!
Heres the latest code i have tried
Object customer = Hotel.Main.Manager.GetMainList(x);
Type frsttype = customer.GetType();
MethodInfo method = frsttype.GetMethod("GetCustomerSpecificData");
MethodInfo generic = method.MakeGenericMethod(frsttype);
String str = generic.Invoke(method);
What i am trying to reach is this method inside the object:
public override string GetCustomerSpecificData()
{
string strout = string.Format("{0,-5}{1,26}{2,28}{3,28}\a", ID, Name, Age, Gender);
string strInfo = Extra;
strout += (string.IsNullOrEmpty(strInfo) ? string.Empty : strInfo);
if (m_specialoffer)
{
strout += string.Format("\nSpecial Offer");
}
if (IsRegularCustomer)
{
strout += (IsDangerus ? "\nIs a regular customer " : "\nIs not a regular customer.");
}
strout += Environment.NewLine + PaymentInfo();
strout += (m_CarPark ? "\nHas car parked in garage." : "\nDoes not have car parked in garage.");
return strout;
}
I hope someone can point me in the correct direction as i dont think i am getting anywhere with this one :/
Any help and hints will be greatly appreciated!!! All will be upvoted for replies!
Regards
There a few things here that you need to do, firstly lets look at the codeyou posted
First question you need t ask youself is Do I need to use reflection, can I instead use interfaces or return a type that I know?
Do you have control of the GetMainList(x)? If so cant you change it so it returns something more useful other then a object?
Object customer = Hotel.Main.Manager.GetMainList(x);
Can you cast to anything?
Secondly your target method is no a generic method so the line below is not going to work.
MethodInfo generic = method.MakeGenericMethod(frsttype);
You are also invoking the method incorrectly you Invoke has two arguments the first one is the target object you wish to invoke the method against and the parameters you can pass into it.
Invoke(object obj, object[] parameters)
To invoke you method you need to the following.
Object customer = Hotel.Main.Manager.GetMainList(x);
Type frsttype = customer.GetType();
MethodInfo method = frsttype.GetMethod("GetCustomerSpecificData");
String str = method.Invoke(customer, null) as string;
There is some great questions and community wikis on stackoverflow and of course there is many tutorials and example in the MSDN library.
A nice tutorial for reflection in .net can be found below.
Reflection in C# Tutorial
i mean you can easy invoke it :
Type myType =customer.GetType();
MethodInfo method = typeof(customer).GetMethod("GetCustomerSpecificData");
MethodInfo generic = method.MakeGenericMethod(myType);
var res= generic.Invoke(this, null);
Closest to what you currently have this could work without relying on reflection.
Object customer = Hotel.Main.Manager.GetMainList(x);
string result="";
var custObj = customer as Customer;
if (custObj !=null)
{
result = custObj.GetCustomerSpecificData();
}
var specialcustObj = customer as SpecialCustomer;
if (specialcustObj !=null)
{
result = specialcustObj.GetCustomerSpecificData();
}
/* etc */
Or, If you can change the implementation of the different types in the List have an interface (or alternative an (abstract) base class.
/* alternatively name it ISpecificData if you want adhere common used standards */
public interface SpecificData
{
string GetCustomerSpecificData();
}
and for your Customer and other classes that can be in the list :
public class Customer:SpecificData
{
/* rest of implemementastion stays the same */
}
Your code to Get a customer would go like this, and will work for every object in the list that implemented the interface.
Object customer = Hotel.Main.Manager.GetMainList(x);
string result="";
var interfaceObj = customer as SpecificData;
if (interfaceObj != null)
{
result = interfaceObj.GetCustomerSpecificData();
}
When you know that only a specific interface will be in the list you can use the generic list to only hold object for that specific type:
mainlist = new List<SpecificData>();
and you can adapt GetMainList to only return the interface SpecificData
Consider the following:
// select a subset of the DataTable
var subset = DataTable.Where(...).Select(row => new
{
Id = Convert.ToInt32(row["Id"]),
Name = row["Name"].ToString(),
Email = row["Email"].ToString()
});
// or create a new object
var subset = new {
Id = 1,
Name = "something random",
Email = "name#domain.tld"
};
Is there any way to use the subset variable as a parameter to a method, without it being cast as a plain Object? Can you somehow carry the auto-generated type of the variable?
I am trying to avoid having to create new classes every time I want to pass LINQ subsets to methods.
Random generic approaches are welcome.
No, passing anonymous types about isn't generally a good idea because you lose the type information*. You should create a concrete type and use that instead.
var subset = DataTable.Where(...).Select(row => new SomeType
{
Id = Convert.ToInt32(row["Id"]),
Name = row["Name"].ToString(),
Email = row["Email"].ToString()
});
Alternatively you can use the Tuple type if you are using .NET 4. This is a simple way to create "disposable" types and still get some type-safety.
*Actually there is a workaround, but I regard it is an ugly hack and would advise that you don't do it.
If I need to do this, I use resharper's "Replace Anonymous Type With Named Class" refactoring option. Then you have an appropriate named type to expose over the API, and you haven't had to do any work. This also gives you options to create it immutable (like anonymous types) or mutable, nested vs top-level, etc.
BTW, I don't recommend struct here (from the question).
Another option is to pass the behaviour into the method - i.e. an Action<int,string,string> callback - then do something like:
foreach(item in query) callback(item);
However, I don't like this as it is not obvious that there is a likely error in:
DoSomething(args, (id, email, name) => Email(To: email, Subject: name));
(the error being that it should probably be (id, name, email), if you see what I mean)
You can use a generic method:
public static void Foo<T>(T item)
{
// Do whatever
}
Then if you call
Foo(subset);
the compiler will infer T for you. Whether or not that actually helps you is another matter... it depends on what the method is meant to do. Obviously Foo can't refer to Id, Name, Email etc.
In general, if multiple methods should know about the same members, then you should use a named type. The usual case for passing them to generic methods is where the method really doesn't care about what type is involved, such as in LINQ.
I've made a feature request for C# 5 that we should be able to create types which have all the same features as anonymous types (immutability, equality, hash code generation, ToString dumping) but for simple named types. We'll see if it actually happens...
Anonymous Types don't provide much help outside of the context they where created.
If you need to pass an Anonymous Type to a method, either this method is very generic like (Example)
void PrintAllObjectProperties(object obj);
witch you would use reflection to do the work, or you are doing something wrong.
Here's what I came up with...
Extension method on Object:
public static class ObjectExtensions
{
/// <summary>
/// Cast Object to anonymous type.
/// E.G.: new Object().ToAnonymousType(new { Property = new Type() });
/// </summary>
public static T ToAnonymousType<T>(this Object o, T t)
{
return (T)o;
}
}
Usage:
public void HandleAnonymousTypeAsParameter(Object o)
{
var anonymousType = o.ToAnonymousType(new
{
Id = new Int32(),
Foo = new String(),
Bar = new String()
});
// ... You can do this in even less characters:
var anonymousType = o.ToAnonymousType(new { Id = 0, Foo = "", Bar = "" });
}
HandleAnonymousTypeAsParameter(new
{
Id = 1,
Foo = "foo",
Bar = "bar"
});
Credits goes to John Skeet and Thomas P.
Sorry for the poor explaination of the problem
Totally rewriting question
I have the following method:
public TReturn FindCached<TSearch, TReturn>(Func<TSearch, TReturn> searchMethod)
where TSearch : ISearchSpecification
where TReturn : class
{
SearchSpecification spec = new GetConcreteSearchSpecification<TSearch>();
//insert magic here to get an attribute from the method on
//the spec class that searchMethod invokes on the line below
return searchMethod(spec);
}
So I have a delegate (searchMethod) and an object (spec) that I want to invoke the delegate on. I want to inspect the object (spec) to find a custom attribute on the method that searchMethod will call when invoked.
Hope this is clearer.
Thanks
Assuming you meant searchMethod to be a variable of type Func<TSearch, TReturn> and mySearchSpec as some implementation of ISearchSpecification<TSearch>, then you are basically asking how to get attributes on a class.
For this, use something like:
object[] attrs = typeof(mySearchSpec).GetCustomAttributes(false);
Assuming that the mySearchSpec type is public, otherwise you may need a different overload for GetCustomAttributes
Addendum:
Based on your revised question, to get the attributes on a method on the actual type of spec used:
Type t = spec.GetType();
MethodInfo m = t.GetMethod("nameOfMethodToBeCalledHere");
object[] attrs = m.GetCustomAttributes(false);
Again, note that you may need overloads for GetMethod or GetCustomAttributes depending on the implementation of the actual class.
Note:
It does seem however like you might be asking for the method called in return searchMethod(spec);, but that is searchMethod and not some method on spec at all.
If you want attributes on searchMethod (nothing to do with spec):
MethodInfo m = searchMethod.Method;
object[] attrs = m.GetCustomAttributes(false);
I think that now covers all permutations of meaning...
This is quite a confusing question, let's see if I have it right:
You have a lambda function (which you describe as a delegate) called searchMethod.
You have a factory-pattern generated object called spec
So you have a method somewhere like this:
[MyCustomAttribute]
public RetClass MyMethod( SearchSpecification input ) {
return input.GetRetClass();
}
And you call this method with:
var result = FindCached( MyMethod );
And in FindCached you want to find MyCustomAttribute - in that case #jerryjvl's answer is right.
Your problem is that you could also do:
var result = FindCached( x => x.GetRetClass() );
I'm not sure from your description whether it's an attribute on the x.GetRetClass() that you actually want. In this case you need to take the lambda apart using expressions, but I wouldn't recommend it - a more complex lambda declaration will result in an anonymous delegate, which is a black-box when you try to parse it at run time.
Instead, as you're using reflection anyway, it might be easier to pass the name of the method that you want instead of the delegate reference:
var result = FindCached( "GetRetClass" );
I ran into a similar situation, jerryjvl's answer explained exactly what I wanted.
I wanted to create a generic profiling method, where I could time how long some method took to run, and retrieve the name of the method using reflection for logging purposes.
The MethodInfo was the key.
Where I have a method like:
public static bool TimeMethod(Func<bool> someMethod)
And then later I want to dynamically get it's name or some attributes off it.
MethodInfo m = someMethod.Method;
object[] attrs = m.GetCustomAttributes(typeof(MonitoringDescriptionAttribute), true);
string name = m.Name;
Cheers
This question already has answers here:
Where do I use delegates? [closed]
(8 answers)
Closed 9 years ago.
I've previously asked a question of about Delegates does anyone have a must have scenario where I would have to use a delegate? How does this improve my C# code?
Just as many scenarios I use it in I've always seem to be able to program around it.
Whenever you're using a Strategy Pattern or an Observer Pattern, delegates make your work much easier than using interfaces.
Nobody mentioned this, but if you are using LINQ with Lambda you use anonymous methods all the time. Which are technically still delegates.
Lets say you have a class called Person
class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
And you wanted to implement a find method where you would find the person based on their FirstName
public Person Where(List<Person> list, string firstName)
{
//find the string
foreach(Person item in list)
if(item.FirstName.Equals(firstName))
return item;
}
This is a very specific search and not very dynamic, meaning if you wanted to search by LastName you would have to change this method or write a new one.
Luckily LINQ provides an extension method called Where to which you need to pass a delegate, which you can create on the fly with the help of anonymous methods.
for instance
string searchString = "Stan";
list.Where( person => person.FirstName.Equals(searchString));
but if you wanted to change to search by LastName you would simply do this
string searchString = "R";
list.Where( person => person.LastName.Equals(searchString));
This example might be not what you were looking for, but I just wanted to show that sometimes we use delegates all the time without thinking about it or realizing it.
Assuming you're not talking about events - of course you can program around it. The point is to make it nicer and cleaner.
protected void Sort()
{
foreach (string key in _dBase.Keys)
{
Array.Sort<Pair<string, Dictionary<string, T>>>(_dBase[key],
new Comparison<Pair<string, Dictionary<string, T>>>(
delegate(Pair<string, Dictionary<string, T>> a, Pair<string, Dictionary<string, T>> b)
{
if (a == null && b != null)
return 1;
else if (a != null && b == null)
return -1;
else if (a == null && b == null)
return 0;
else
return a.First.CompareTo(b.First);
}));
}
}
Could I do that without an inline delegate? Sure. Would I have a floppy private method in my class that would only be used for this one instance? Yup.
Edit: As mentioned in the comments, you can simplify:
Array.Sort<Pair<string, Dictionary<string, T>>>(_dBase[key],
new Comparison<Pair<string, Dictionary<string, T>>>(
delegate(Pair<string, Dictionary<string, T>> a, Pair<string, Dictionary<string, T>> b)
{
to
Array.Sort<Pair<string, Dictionary<string, T>>>(_dBase[key], (a,b) =>
{
If you imagine C# without delegates, you would commonly encounter situations where you have classes or interfaces with one method. The name of that method is redundant. e.g.
public interface IGetMail
{
Mail JustGetTheMail();
}
The interface is all about that one method. A reference to an object of that type is really no more than a reference to a single callable method. The calling code:
Mail m = getMail.JustGetTheMail();
could be abreviated to:
Mail m = getMail();
The compiler could do that as "syntactic sugar" without any ambiguity, because there's only one method you could possible call on that getMail reference.
So let's add that feature to our C# compiler. Now, when declaring these types, we could make that a little neater as well. We don't need to specify the method name when calling it, so why should we have to give the method a name in the first place?
Let's pick a standard method name, Invoke, i.e.
public interface IGetMail
{
Mail Invoke();
}
We'll add some more syntactic sugar to allow us to write that as:
public delegate Mail GetMail();
Hey presto. We've added delegates to our C# compiler.
(Technically the CLR is also aware of delegates, and so rather than generating an interface, the C# compiler generates a special "delegate" type, which has support for asynchronous invocation, and manipulating an immutable list of delegates and treating them as a single reference; but in a basic form it could have been done with interfaces. There is a proposal to do that for Java).
We can then go further and add anonymous delegates - making them succinct to implement in a way that interfaces are not.
So to answer your question - any time where your interface has one method, it could be a delegate, and you'll be able to seriously cut down the amount of junk code you have to write.
Real World Usage:
Assume you have a simple user control called Checker- which comprises of just 2 checkboxes - chkA and chkB.
Within the user-control you can control the check/uncheck events by implementing the respective events fro chkA and ChkB
3.Now, in a new Win Form , when you drag in Checker...and your objective is to make sure that when chkA is clicked you have to change the background color of a label...lblColorPicker.
Mind you lblColorPicker is a control that exists in the form and is not directly bound to Checker.
How will you achieve this?
Answer:
First, you have to create a new event for your user control Checker.
To create this new event the first thing would be to create a delegate in you user control and then use this delegate as a defining type for the new event you are writing.
And then this Event has to be mapped to chkA...event in your user control.
This way you can control chkA...through the new event from any new form by referencing the event...through a delegate you just wrote.
So, much for Real-World usage!
This objective CANNOT be achieved with out writing the delegate.
Let me know if you think otherwise...or if you need more elaboration.
Anonymous delegates make the code much more readable in some case (you don't have to go to another method to see code that belongs to your method:
Winforms example
class MyForm:Form{
//...
protected override void OnLoad(EventArg e){
this.Cursor=Cursors.Wait();
this.Enabled=false;
// do a long running DB operation without blocking the UI Thread
ThreadPool.QueueUserWorkItem(state=>{
DoLongDBOperation();
// re enable the form
BeginInvoke(new Action(()=>{ this.Cursor=Cursors.Default;this.Enabled=true;}));
});
}
Delegates are an absolute must-have if you add events to your class or are doing anything asynchroneous (There are several other good reasons to have delegates). Benefits is that it is a very flexible approach.
I think you are referring to defining custom delegates?
EventHandler has minimised the requirement for custom delegate definitions but they are still useful if you want to add additional parameters to the method signature.
Whenever you need to modify or change any properties of a winForms control, you need to use a delegate to pass control back to the thread the control was created on... to name just one of many examples.
One example would be a pub/sub message dispatcher. You would need to register the events with the dispatcher and then call them appropriately. This allows you to connect disparate pieces of your code quite easily. I can't think of a way to do this without using delegates
Say you have a console application that responds to different key presses:
Action a = () => {/* Do Stuff*/};
Action b = () => {/* Do Stuff*/};
Action c = () => {/* Do Stuff*/};
Action d = () => {/* Do Stuff*/};
Action e = () => {/* Do Stuff*/};
Action f = () => {/* Do Stuff*/};
Action g = () => {/* Do Stuff*/};
Action h = () => {/* Do Stuff*/};
Action i = () => {/* Do Stuff*/};
Action j = () => {/* Do Stuff*/};
List<Action> actions = new List<Action>() {a,b,c,d,e,f,g,h,i,j};
string line;
while((line = Console.ReadKey().KeyChar) != 'q')
{
if(line.isBetween_0_and_9())
{
actions[line.ParseInt()]();
}
}
You can obviously just use a bunch of Ifs but this not only easier but it's almost certainly more clear/readable.