C# vs Objective C

/* Posted March 16th, 2008 at 5:26pm    */
/* Filed under Apple, C#, Windows    */

Sharp and Objective
This may come as a surprise for some people, but I’ve been using C# and Objective-C both for almost exactly as long (between three and four years). What you might call my current day job is comprised of writing C# code.

Here’s the thing with .NET. .NET is great. It’s not great as the One True Platform for Windows development. On the other hand, it’s not “great for something that’s from Microsoft” either. It is great on its own merits, perhaps, and it is a shame that its merits are so tightly coupled to provide an excuse for C# to exist, because they are each shackled to each other.

.NET is now, and has been for a while, at least perceptibly noticeably faster than Java. I’m personally way better off with C# than with Java. What I think I’m doing at this stage is establishing that I really quite like .NET and C# both, and that even if I didn’t, I’d think they were more than reasonable efforts for a technology-of-the-week company like Microsoft.

That said, there’s a big technology – and philosophy – divide between C# and Objective-C, and between .NET and Cocoa. Coding in both of them means that I’m soaking both my feet in water of different temperatures. I am in a fairly unique position to stand with one foot in a bucket of cold water and one foot in a bucket of hot water and announce how, on average, I’m feeling pretty good. To wit: When I’m in C#, I end up missing Objective-C. When I’m in Objective-C, I end up missing C#. Here, therefore, are some reflections, most prompted by a chat I had with Scott earlier today.

I love generics. This appeared gradually and surprised me at first, but it’s quite true. Generics, if you don’t know, means that you can supply types to classes when using them – like List<string> or Dictionary<string,int> – and have instances whose methods change to accept those types. I’m generally firmly in the duck typing camp, but C# and Objective-C both need compiling, and when there’s a compiling stage, you can get warnings and errors. The Objective-C code for storing, say, a string in an array, makes it impossible to know when you’re passing in non-strings without doing type checks manually beforehand, or creating tuned methods that only accept the given type in order to generate the standard compiler error.

C#:
List<string> strings = new List<string>();
strings.Add(“xyzzy”); // takes only strings
strings.Add(15); // compiler error
string x = strings[0]; // guaranteed to be a string
// or non-existant (yielding an exception)
strings.Remove(0);

Objective-C:
NSMutableArray *strings = [[NSArray array] mutableCopy];
[strings addObject:@"xyzzy"];
[strings addObject:[NSNumber numberWithInt:15]];
string x = [strings objectAtIndex:0];
[strings removeObjectAtIndex:0];

The previous example brings to mind another wider look at the same subject – lists. In Objective-C, you get a fairly simple choice for an array: mutable or immutable (default). Being the strict C superset that it is, you can also drop down into C arrays and pointer trickery.

C#, on the other hand… Someone at Microsoft enjoys having a dozen deceptively similar – but ultimately all a little different – ways to solve this problem.

At the bottom end, you have C# arrays, which are similar to C arrays. They are fixed-size and immutable. They’re also alone in being natively initializable via literals. A low-level class, System.Array handles the juggling, and each Array is typed for a specific type but using a special, C array-like syntax (string[]), instead of generics syntax. This is because generics were introduced in C# 2.0 and .NET 2.0, and the Array has been along (with the same special treatment) since the very beginning.

Slightly above C# arrays are non-generic lists. The ArrayList is an almost perfect parallel to Cocoa’s NSMutableArray – something that stores only objects. You can store anything in an ArrayList. These were the only mutable all-purpose lists in .NET 1.0 and 1.1.

Above ArrayList lie generic lists – List<T>, where T is to be substituted to a type you pass in when you instantiate, subclass from or otherwise use the class.

So far, so good. What’s wrong with the hierarchy is everything above this point. Imagine for a second that you have a control called a ListView. (Hint: Microsoft does.) Imagine further that you want a property inside to return the list of items for your pursual. Imagine also that these items are all of class ListViewItem. If you were Microsoft, you might want to use, simply, List<ListViewItem>. Too bad you’re not Microsoft, and too bad ListView was around in .NET 1.0, way before generic lists.

In these kinds of situations, there are special purpose lists (belonging to some sort of fuzzily defined grouping called Collections meaning basically “objects that have other objects in them”). In fact, there are fields, endless fields, where Collections are no longer being born, they are grown. So what do you end up with? ListView.ListViewItemCollection – a special-purpose class if you ever had one – specifically designed to hold ListViewItems. To be fair, ListViewItemCollection has at least one speciality – that of being able to access ListViewItems by passing their Key property as the indexer – literally like collection["key"].

Just having several hundred very special-purpose classes isn’t a valid point of criticism, though. The strange thing is that many of them drop (or choose not to implement) small parts of functionality. Some might not ‘do’ enumeration (the technology that enables foreach loops), some might leave out Contains-like functionality to check for the existance of a value. It’s all very confusing and a very poor and uneven experience for the programmer, especially since the class’s raison d’être is often deeply hidden in the documentation, if mentioned at all.

As much as I like generic lists, I despise most of the rest of the trickery.

Boxing! C# gets boxing (wrapping non-objects in objects) right – int and double and string and friends are supposedly primitives, but are surprisingly well-mapped back and forth to real classes to the point where you really can’t tell unless you’re profiling. In Objective-C, on the other hand, the boxing process is entirely manual, like: NSNumber *integer = [NSNumber numberWithInt:8]; int x = [integer intValue];

C# has constructors. Objective-C has alloc-init method pairs, designed to be called in a chain, or class convenience methods for doing both and returning an autoreleased object for you. When you call a constructor in C#, it first creates the object for you and then calls the appropriate constructor, whose objective it is to set up the object, already assigned to this. In Objective-C, you’re responsible for returning the object self. In both languages, you’re responsible for calling the appropriate superclass constructors.

The C# way involves less manual labor and book-keeping. In Objective-C, you can return an object that has already been initialized, say, if you’re keeping a cache of objects not supposed to be created twice, instead of the object just created.

There’s no correct answer, but the different approaches will be sure to have you curse at one time or another.

The class models in C# and Objective-C are vastly different.

Objective-C has bona-fide class methods, eligible for specification in Objective-C protocols. C# has static methods, not eligible for specification in C# interfaces. Objective-C does not have class variables, but it does inherit static variables from C, which works almost the same. C#’s doodads are also called static variables. In Objective-C, no instance variable can be assigned immediately on declaration, but must be instantiated in the init instance method; in C#, both static and instance variables can be assigned immediately, and to the return value of any statement, including functions. (Since Objective-C inherits C’s static variables, you can only assign literal values to them on declaration, and not return values of methods or functions.)

C# has dynamic dispatch off by default – it’s opt-in by the use of the virtual keyword in the method declaration and the override keyword in the ‘overriding’ method. Classes can be prevented from containing virtual methods by being declared with the sealed keyword. It’s also possible to declare abstract classes, which have abstract methods that have to be overridden and use dynamic dispatch. Objective-C has dynamic dispatch on by default. (Dynamic dispatch is being able to define the method foo in class A, introducing class B that inherits from class A, implementing method foo in class B, and having class B called when you call the method foo on class B.)

Finally, C#, like Java before it, and Objective-C before it, uses a policy of one superclass, n interfaces/protocols. In Objective-C, the way of specifying an interface type is ObjectClass<Interface>; in C#, it’s just Interface. (C# has one root class, System.Object, which everything implicitly inherits from by default; Objective-C allows defining new root classes and provides the id keyword – in actuality the runtime’s data structure of an object – to specify ‘any object whatsoever’.)

There’s a lot of ground to cover on C# and Objective-C, and on .NET and Cocoa. The origins and fates of .NET and Cocoa are, dates taken out of the equation, remarkably similar: objective-oriented juggernauts gaining traction with smaller and newer apps, but largely failing with the cross-platform market and the big, heavy, thousand-year apps.

What best sums up my feelings about Objective-C and C# is this: Objective-C+Cocoa is smaller and better defined, and scores a higher average level on the graph. However, C#+.NET’s extremes – both good and bad – are more pronounced. At its best, it’s above Objective-C+Cocoa by a good deal; at its worst, it’s right down the chasm with no hope of improving.

I am certainly hoping that both teams are watching each other with open minds.

taken from http://waffle.wootest.net/2007/03/30/sharp-and-objective/

Syntax

Objective-C is a very “thin” layer on top of C. Objective-C is a strict superset of C. That is, it is possible to compile any C program with an Objective-C compiler. Objective-C derives its syntax from both C and Smalltalk. Most of the syntax (including preprocessing, expressions, function declarations, and function calls) is inherited from C, while the syntax for object-oriented features was created to enable Smalltalk-style message passing.

 Messages

The added syntax is for built-in support of object-oriented programming. The Objective-C model of object-oriented programming is based on sending messages to objects, similar to the model of Smalltalk. This is unlike the Simula programming model, which is used by C++ among other programming languages. This distinction is semantically important. The basic difference is that in Objective-C, one does not call a method; one sends a message.

An object called obj whose class has a method doSomething implemented is said to respond to the message doSomething. If we wish to send a doSomething message to obj, we write

 

[obj doSomething];

whereas in C++ we write

 

obj.doSomething();

This mechanism allows messages to be sent to an object even if the object is not able to respond to them. This differs from statically typed languages such as C++ and Java in which all method calls to objects must be predefined. (See the dynamic typing section below.)

Interfaces and implementations

Objective-C requires the interface and implementation of a class to be in separately declared code blocks. By convention, the interface is put in a header file and the implementation in a code file; the header files, suffixed .h, are similar to C header files.

Interface

The interface of the class is usually defined in a header file. Convention is usually to create the name of the header file based on the name of the class. So if we have the class Thing, Thing’s interface goes in the file Thing.h.

The interface declaration is in this form:

 

@interface classname : superclass name
 {
    instance variables
 }
 + classMethod1;
 +(return_type) classMethod2;
 +(return_type) classMethod3:(param1_type)parameter_varName;

 -(return_type) instanceMethod1:(param1_type)param1_varName :(param2_type)param2_varName;
 -(return_type) instanceMethod2WithParameter:(param1_type)param1_varName andOtherParameter:(param2_type)param2_varName;
 @end

Hyphens mark instance methods and plus signs mark class methods (like static member functions in C++). This is different from the meaning of a preceding – and + in UML diagrams which mean private and public method, respectively.

Return types can be any standard C type such as void which indicates that no value is returned, primitive types such as float, int and BOOL, and pointers to Objective-C classes such as NSArray *, NSString *, and NSImage *. In Objective-C, the return type id represents a pointer to an arbitrary object. If no return type is specified as with classMethod1, the return type is assumed to be id.

In all methods, parameters are defined with a colon followed by the expected parameter type in parentheses and the parameter name. In some cases it is useful to add descriptive text before each parameter, and in some cases it is unnecessary. When working with large projects it is often very useful to have long, descriptive method names that make it easier to determine how each parameter is used.

 

-(void) setRange:(int)start :(int)end;

 -(void) importDocumentWithName:(NSString *)name withSpecifiedPreferences:(Preferences *)prefs beforePage:(int)insertPage;

Implementation

The interface only declares the prototypes for the methods, and not the methods themselves, which go in the implementation. The implementation is usually stored in a main file, for example, Thing.m. The implementation is written

 

@implementation classname
 + classMethod
 {
    // implementation
 }

 - instanceMethod
 {
    // implementation
 }
 @end

Methods are written in a different way from C-style functions. For example, a function in both C and Objective-C follows this general form:

 

int do_something(int i)
 {
    return square_root(i);
 }

with int do_something(int) as the prototype.

When this is implemented as a method, this becomes:

 

- (int) do_something:(int) i
 {
    return [self square_root: i];
 }

A more canonical way of writing the above method would be like this, by naming the first argument in the selector name:

 

- (int) doSomethingWithInt: (int) i
 {
    return [self squareRootOfInt:i];
 }

This syntax may appear to be more troublesome but it allows pseudo-naming of parameters, for example

 

- (int) changeColorWithRed:(int) r  green:(int) g  blue:(int) b

which can be invoked thus:

[myColor changeColorWithRed:5 green:2 blue:6];

Internal representations of this method vary between different implementations of Objective-C. If myColor is of the class Color, internally, instance method -changeColorWithRed:green:blue: might be labeled _i_Color_changeColorWithRed_green_blue. The i is to refer to an instance method, with the class and then method names appended, colons translated to underscores. As the order of parameters is part of the method name, it cannot be changed to suit coding style or expression as in true named parameters.

However, internal names of the function are rarely used directly, and generally even message-sends are converted to a call to a function defined in a run-time library rather than directly accessing the internal name. This is partially because it is rarely known at compile-time which method will actually be called, because the class of the receiver (i.e., the object being sent the message) is rarely known until runtime.

Instantiation

Once an Objective-C class is written, it can be instantiated. This is done by first allocating the memory for a new object and then by initializing it. An object isn’t fully functional until both steps have been completed. These steps are typically accomplished with a single line of code:

 

MyObject * o = [[MyObject alloc] init];

The alloc call allocates enough memory to hold all the instance variables for an object, and the init call can be overridden to set instance variables to specific values on creation. The init method is often written as follows:

 

-(id) init {
    self = [super init];
    if (self) {
        ivar1 = value1;
        ivar2 = value2;
        .
        .
        .
    }
    return self;
}

Protocols

Objective-C was extended at NeXT to introduce the concept of multiple inheritance of specification, but not implementation, through the introduction of protocols. This is a pattern achievable either as an abstract multiply inherited base class in C++, or else, more popularly, adopted (e.g., in Java or C#) as an “interface”. Objective-C makes use of both ad-hoc protocols, called informal protocols, and compiler enforced protocols called formal protocols.

An informal protocol is a list of methods which a class can implement. It is specified in the documentation, since it has no presence in the language. Informal protocols often include optional methods, where implementing the method can change the behavior of a class. For example, a text field class might have a delegate which should implement an informal protocol with an optional autocomplete method. The text field discovers whether the delegate implements that method (via reflection), and, if so, calls it to support autocomplete.

A formal protocol is similar to an interface in Java or C#. It is a list of methods which any class can declare itself to implement. The compiler will emit an error if the class does not implement every method of its declared protocols. The Objective-C concept of protocols is different from the Java or C# concept of interfaces in that a class may implement a protocol without being declared to implement that protocol. The difference is not detectable from outside code. Formal protocols cannot provide any implementations, they simply assure callers that classes which conform to the protocol will provide implementations. In the NeXT/Apple library, protocols are frequently used by the Distributed Objects system to represent the capabilities of an object executing on a remote system.

The syntax

 

@protocol Locking
 - (void)lock;
 - (void)unlock;
 @end

denotes that there is the abstract idea of locking which is useful, and when stated in a class definition

 

@interface SomeClass : SomeSuperClass <Locking>
 @end

denotes that instances of SomeClass will provide an implementation for the two instance methods using whatever means they want. This abstract specification is particularly useful to describe the desired behaviors of plug-ins for example, without constraining at all what the implementation hierarchy should be.

Dynamic typing

Objective-C, like Smalltalk, can use dynamic typing; we can send an object a message not specified in its interface. This can allow for increased flexibility — in Objective-C an object can “capture” this message, and depending on the object, can send the message off again to a different object (who can respond to the message correctly and appropriately, or likewise send the message on again). This behavior is known as message forwarding or delegation (see below). Alternatively, an error handler can be used instead, in case the message cannot be forwarded. If the object does not forward the message, handle the error, or respond to it, a runtime error occurs.

Static typing information may also optionally be added to variables. This information is then checked at compile time. In the following statements, increasingly specific type information is provided. The statements are equivalent at runtime, but the additional information allows the compiler to warn the programmer if the passed argument does not match the type specified. In the first statement, the object must conform to the aProtocol protocol, and in the second, it must be a member of the NSNumber class.

 

- setMyValue:(id <aProtocol>) foo;
 - setMyValue:(NSNumber*)foo;

Dynamic typing can be a powerful feature. When implementing container classes using statically-typed languages without generics like pre-1.5 Java, the programmer is forced to write a container class for a generic type of object, and then cast back and forth between the abstract generic type and the real type. Casting however breaks the discipline of static typing – if you put in an Integer and read out a String, you get an error. One way of alleviating the problem is to resort to generic programming, but then container classes must be homogeneous in type. This need not be the case with dynamic typing.

Forwarding

Since Objective-C permits the sending of a message to an object which might not respond to it, the object has a number of things it can do with the message. One of these things could be to forward the message on to an object which can respond to it. Forwarding can be used to implement certain design patterns, such as the Observer pattern or the Proxy pattern very simply.

The Objective-C runtime specifies a pair of methods in Object

  • forwarding methods:

 

- (retval_t) forward:(SEL) sel :(arglist_t) args; // with GCC
 - (id) forward:(SEL) sel :(marg_list) args; // with NeXT/Apple systems
  • action methods:

 

- (retval_t) performv:(SEL) sel :(arglist_t) args;  // with GCC
 - (id) performv:(SEL) sel :(marg_list) args; // with NeXT/Apple systems

and as such an object wishing to implement forwarding needs only to override the forwarding method to define the forwarding behaviour. The action methods performv:: need not be overridden as this method merely performs the method based on the selector and arguments.

Example

Here is an example of a program which demonstrates the basics of forwarding.

Forwarder.h

 

#import <objc/Object.h>

 @interface Forwarder : Object
 {
    id recipient; //The object we want to forward the message to. 
 }

 //Accessor methods
 - (id) recipient;
 - (void) setRecipient:(id) _recipient; 

 @end
Forwarder.m

 

#import "Forwarder.h"

 @implementation Forwarder

 - forward: (SEL) sel : (marg_list) args
 {
    /*
     * Check whether the recipient actually responds to the message.
     * This may or may not be desirable, for example, if a recipient
     * in turn does not respond to the message, it might do forwarding
     * itself.
     */
    if([recipient respondsTo:sel])
       return [recipient performv: sel : args];
    else
       return [self error:"Recipient does not respond"];
 }

 - (id) setRecipient: (id) _recipient
 {
    recipient = _recipient;
    return self;
 }

 - (id) recipient
 {
    return recipient;
 }
 @end
Recipient.h

 

#import <objc/Object.h>

 // A simple Recipient object.
 @interface Recipient : Object
 - (id) hello;
 @end
Recipient.m

 

#import "Recipient.h"

 @implementation Recipient

 - (id) hello
 {
    printf("Recipient says hello!\n");

    return self;
 }

 @end
main.m

 

#import "Forwarder.h"
 #import "Recipient.h"

 int
 main(void)
 {
    Forwarder *forwarder = [Forwarder new];
    Recipient *recipient = [Recipient new];

    [forwarder setRecipient:recipient]; //Set the recipient. 
    /*
     * Observe forwarder does not respond to a hello message! It will
     * be forwarded. All unrecognized methods will be forwarded to
     * the recipient
     * (if the recipient responds to them, as written in the Forwarder)
     */
    [forwarder hello]; 

    return 0;
 }

Notes

If we were to compile the program, the compiler would report

$ gcc -x objective-c -Wno-import Forwarder.m Recipient.m main.m -lobjc
main.m: In function `main':
main.m:12: warning: `Forwarder' does not respond to `hello'
$

The compiler is reporting the point made earlier, that Forwarder does not respond to hello messages. In certain circumstances, such a warning can help us find errors, but in this circumstance, we can safely ignore this warning, since we have implemented forwarding. If we were to run the program

$ ./a.out
Recipient says hello!

Categories

Cox’s main concern was the maintainability of large code bases. Experience from the structured programming world had shown that one of the main ways to improve code was to break it down into smaller pieces. Objective-C added the concept of Categories to help with this process.

A category collects method implementations into separate files. The programmer can place groups of related methods into a category to make them more readable. For instance, one could create a “SpellChecking” category “on” the String object, collecting all of the methods related to spell checking into a single place.

Furthermore, the methods within a category are added to a class at runtime. Thus, categories permit the programmer to add methods to an existing class without the need to recompile that class or even have access to its source code. For example, if the system you are supplied with does not contain a spell checker in its String implementation, you can add it without modifying the String source code.

Methods within categories become indistinguishable from the methods in a class when the program is run. A category has full access to all of the instance variables within the class, including private variables.

Categories provide an elegant solution to the fragile base class problem for methods.

If you declare a method in a category with the same method signature as an existing method in a class, the category’s method is adopted. Thus categories can not only add methods to a class, but also replace existing methods. This feature can be used to fix bugs in other classes by rewriting their methods, or to cause a global change to a class’ behavior within a program. If two categories have methods with the same method signature, it is undefined which category’s method is adopted.

Other languages have attempted to add this feature in a variety of ways. TOM took the Objective-C system a step further and allowed for the addition of variables as well. Other languages have instead used prototype oriented solutions, the most notable being Self.

Example usage of categories

This example builds up an Integer class, by defining first a basic class with only accessor methods implemented, and adding two categories, Arithmetic and Display, which extend the basic class. Whilst categories can access the base class’ private data members, it is often good practice to access these private data members through the accessor methods, which helps keep categories more independent from the base class. This is one typical usage of categories—the other is to use categories to add or replace certain methods in the base class (however it is not regarded as good practice to use categories for subclass overriding).

Integer.h

 

#include <objc/Object.h>

 @interface Integer : Object
 {
    int integer;
 }

 - (int) integer;
 - (id) integer: (int) _integer;
 @end
Integer.m

 

#import "Integer.h"

 @implementation Integer
 - (int) integer
 {
    return integer;
 }

 - (id) integer: (int) _integer
 {
    integer = _integer;
 }
 @end
Arithmetic.h

 

#import "Integer.h"

 @interface Integer (Arithmetic)
 - (id) add: (Integer *) addend;
 - (id) sub: (Integer *) subtrahend;
 @end
Arithmetic.m

 

#import "Arithmetic.h"

 @implementation Integer (Arithmetic)
 - (id) add: (Integer *) addend
 {
    return [self integer: [self integer] + [addend integer]];
 }

 - (id) sub: (Integer *) subtrahend
 {
    return [self integer: [self integer] - [subtrahend integer]];
 }
 @end
Display.h

 

#import "Integer.h"

 @interface Integer (Display)
 - (id) showstars;
 - (id) showint;
 @end
Display.m

 

#import "Display.h"

 @implementation Integer (Display)
 - (id) showstars
 {
    int i, x = [self integer];
    for(i=0; i < x; i++)
       printf("*");
    printf("\n");

    return self;
 }

 - (id) showint
 {
    printf("%d\n", [self integer]);

    return self;
 }
 @end
main.m

 

#import "Integer.h"
 #import "Arithmetic.h" 
 #import "Display.h"

 int
 main(void)
 {
    Integer *num1 = [Integer new], *num2 = [Integer new];
    int x;
    printf("Enter an integer: ");
    scanf("%d", &x);
    [num1 integer:x];
    [num1 showstars];
    printf("Enter an integer: ");
    scanf("%d", &x);
    [num2 integer:x];
    [num2 showstars];

    [num1 add:num2];
    [num1 showint];
 }

Tags:


9 Responses to “C# vs Objective C”

  • Comment from Randy Sutton

    Very good write-up!

    Coming from the world of .NET, I have been struggling to understand the concepts behind Objective-C. The comparative approach that has been taken here has really helped me grasp some of the key concepts behind Objective-C and how it’s powerful features come into play.

    Thank you!

  • Comment from mr.H

    Great article! I’m a total Objective-C noob but this helped me understand a lot of things. Thank you!

  • Comment from Tarik Tiré

    Great write-up

    Thank you

  • Comment from Quinn Taylor

    Would it be possible to fix your text so the ‘ and ” marks aren’t some screwy character sequence like ’? It detracts somewhat from the readability of the article.

    Also, your NSMutableArray example uses an odd construct and leaks memory — better to use [NSMutableArray array] instead — and the 4th line is a compile error when trying to declare a string. Also, the last line of the preceding C# example is a compile error — it should use .RemoveAt() instead of Remove() to remove an object at a given index.

  • Comment from Sunit Joshi

    Very nice write up. I’m just graduating to ObjC from C# and found this very enlightening. Hope to see more on .Net/ObjC comparisons.

    thanks
    Sunit

  • Comment from zuuk kaths

    By MICHAEL B. ORENPublished: October 13, 2010CASI 63 years after the United Nations recognized the Jewish people’s right to independence in their homeland – and more than 62 years since the creation of Israel – the Palestinians are still denying the Jewish state. Read More » Automatically translated from Spanish »

  • Comment from lan maries

    ??????????? ?????.

  • Comment from wald

    I have found cheap ones are usually better and are usually multi-region, compared to named brands! I have my Sony one in storage and use a cheap unknown brand.

  • Comment from Arakun

    Nice comparison, though it seems to primarily be directed at people moving from C# to Objective-C. As someone trying to move in the other direction it’d be nice to have some explanations of C# concepts that might appear alien to an Objective-C programmer.


Leave a Reply

or Login (not required)





HTML tags allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>